zoukankan      html  css  js  c++  java
  • UVa 714

    链接:

    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
    ", 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 }
  • 相关阅读:
    C# NPOI 导入与导出Excel文档 兼容xlsx, xls(xf13中已经引用了xlsx的npoi)
    ASP.Net超时时间已到解决办法-
    解决SqlDataSource连接超时的问题
    SqlDataSource控件超时的困惑
    Redis+Keepalived
    Linux Ubuntu 16.04 安装步骤+远程环境
    JDK 安装
    Maven 安装
    CAT 默认密码修改
    CAT 监控搭建
  • 原文地址:https://www.cnblogs.com/hkxy125/p/8136696.html
Copyright © 2011-2022 走看看