UVa 714 - Copying Books

链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=655

 

题意:

把一个包含m个正整数的序列划分成k个(1≤k≤m≤500)非空的连续子序列,使得每个正整数恰好属于一个序列。
设第i个序列的各数之和为S(i),你的任务是让所有S(i)的最大值尽量小。
例如,序列1 2 3 2 5 4划分成3个序列的最优方案为1 2 3 | 2 5 | 4,其中S(1)、S(2)、S(3)分别为6、7、4,
最大值为7;如果划分成1 2 | 3 2 | 5 4,则最大值为9,不如刚才的好。每个整数不超过1e7。
如果有多解,S(1)应尽量小。如果仍然有多解,S(2)应尽量小,依此类推。

 

分析:

下面考虑一个新的问题:能否把输入序列划分成m个连续的子序列,使得所有S(i)均不超过x?
将这个问题的答案用谓词P(x)表示,则让P(x)为真的最小x就是原题的答案。
P(x)并不难计算,每次尽量往右划分即可(想一想,为什么)。
接下来又可以猜数字了——随便猜一个x0,如果P(x0)为假,那么答案比x0大;
如果P(x0)为真,则答案小于或等于x0。至此,解法已经得出:二分最小值x,把优化问题转化为判定问题P(x)。
设所有数之和为M,则二分次数为O(logM),计算P(x)的时间复杂度为O(n)(从左到右扫描一次即可)。

 

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 typedef long long int LLI;
 5 
 6 int n, k, a[500+5];
 7 
 8 bool judge(LLI L){
 9     int amount = 0;
10     LLI sum = 0;
11     for(int i = 0; i < n; i++){
12         if(sum + a[i] > L){
13             amount++;
14             sum = 0;
15         }
16         sum += a[i];
17     }
18     return amount <= k - 1;
19 }
20 
21 void output(LLI L){
22     bool last[500+5]; //last[i] 表示 a[i] 是否是某个划分的最后一个元素
23     memset(last, false, sizeof(last));
24     int remain = k;
25     LLI sum = 0;
26     for(int i = n - 1; i >= 0; i--){
27         if(sum + a[i] > L || i + 1 < remain){
28             last[i] = true;
29             remain--;
30             sum = 0;
31         }
32         sum += a[i];
33     }
34 
35     for(int i = 0; i < n - 1; i++){
36         printf("%d ", a[i]);
37         if(last[i]) printf("/ ");
38     }
39     printf("%d\n", a[n-1]);
40 }
41 
42 int main(){
43     int T;
44     scanf("%d", &T);
45     while(T--){
46         int up = -1;
47         LLI sum = 0;
48         scanf("%d%d", &n, &k);
49         for(int i = 0; i < n; i++){
50             scanf("%d", &a[i]);
51             if(up < a[i]) up = a[i];
52             sum += a[i];
53         }
54 
55         LLI L = up, R = sum;
56         while(L < R){
57             LLI M = L + (R - L) / 2;
58             if(judge(M)) R = M;
59             else L = M + 1;
60         }
61 
62         output(L);
63     }
64     return 0;
65 }

 

posted @ 2017-12-28 17:11  Ctfes  阅读(285)  评论(0编辑  收藏  举报