Problem G
合并模板
XDU Fate 有 n 个 ACM/ICPC 比赛的模板,每个都是一个独立的 PDF 文
件。为了便于打印,万神希望将这些模板合并成一个 PDF 文件。万神有一个工
具,可以将至多 k 个 PDF 文件合并为 1 个,合并后的文件大小是原来 k 个文
件的大小之和。万神发现,这个工具每次运行的时间正比于输出文件的大小。设
每输出 1KB 需要 1 单位时间,那么万神至少要多少时间才能合并完所有的文件
呢?
输入格式
输入文件包含多组数据(最多 100 组),请处理到文件结束。
每组数据包含 2 行,第 1 行包含两个整数 n、k,用空格分割。
第二行包含 n 个整数 s 1 · · · s n ,用空格分割,表示原始的 n 个模板文件的大
小(单位为 KB)。
保证 1 ≤ n ≤ 1000,2 ≤ k ≤ 1000,1 ≤ s i ≤ 10 9 。
输出格式
对于每组数据输出 1 行,表示合并所有文件需要的最短时间。
输入样例
7 4
1 2 3 4 5 6 7
3 5
1 2 3
输出样例
38
6
样例解释
对于第一组样例,首先合并前 4 个文件,耗费 10 单位时间。之后把生成的
大小 10KB 的文件和后 3 个文件合并,耗费 28 单位时间,共计 38 单位时间。不
存在时间更少的合并方案。
对于第二组样例,可以一次合并所有文件。
HINT
对于较大的数据,你可能需要使用 64 位整数。
思路:
非连续,各数不定的石子归并,但是与石子归并八竿子不打,相当于可移动的,可一次合并多个的石子归并,
主要是贪心的方法,每次取当前堆中最小的k堆进行合并,一定能保证合并的总和最小,原则是大文件合并次数越少越好,而且每次合并的文件个数越多越好,
光按上述思想会Wrong
例如:共6个文件 ,一次最多合并4个,每个文件大小分别为: 1 2 3 4 5 6按上述方法进行合并:
找最小4个:1, 2, 3, 4 求和 = 10 再将5 6 10 进行合并,求和=11,总共时间11+10 = 21, 再看:现将1 2 3合并得6 再将 4, 5, 6, 6 合并,得11 ,总时间 11+6 = 17<21,那么将怎么考虑呢?
首先,如果给出的文件数,恰好可以m次合并成一个即 n %(k-1) == 1时,上述方法正确,否则不正确,,所以要将文件数补成使其恰好可以经过m次每次合并k个文件,合并为1件,怎么补?肯定补0嘛,或者,将最小的几个(<k) 合并为1个之后,使之剩余的加上这一个可以完美合并,再进行合并, 这样就可以求得最小,因为大的合并次数越少越好,保证大的参与的合并一定有k个文件.
下面采用补0的方法进行合并,,注意当k==2时 任意n%(k-1) 即 n%(1) 永远不可能为1.而造成段错误,鄙人一开始大意疏忽没有考虑倒k=2时,导致RE了n次...
代码:
/*************************************************************************
> File Name: g.cpp
> Author: dulun
> Mail: dulun@xiyoulinux.org
> Created Time: 2016年04月21日 星期四 12时53分26秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 999909;
LL a[N+N];
int main()
{
int n, k;
while(cin>>n>>k)
{
memset(a, 0, sizeof(a));
for(int i = 0; i < n; i++)
scanf("%lld", &a[i]);
if(n == 1)
{
printf("%lld\n", a[0]);
continue;
}
if(k != 2)
{
while(n % (k-1) != 1)
a[n++] = 0;
}
sort(a, a+n);
int m = 0;
LL ans = 0;
while(m <= n-k)
{
for(int i = 0; i < k-1; i++)
{
a[m+k-1] += a[i+m];
}
ans += a[m+k-1];
m += k-1;
sort(a, a+n);
}
cout<<ans<<endl;
}
return 0;
}