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

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值