2016西电校赛网络赛 Problem G 合并模板

2019-04-14 08:41发布

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 #include #include #include #include #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 ", 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<return 0; }