zoukankan      html  css  js  c++  java
  • P1182 数列分段 Section II

    很容易被题目举的例子迷惑,其实贪心是对的.后文再提.

    使用如下的二分写法:

        while(l < r){
            int mid = r + l >> 1;
            if(C(mid)) r = mid;
            else l = mid + 1;
        }
    
        printf("%d
    ", l);

    函数C(x)检查以x为最大值能否把数列分成m段,如果可以则返回真,否则返回假.显然,如果使用贪心能够在少于m段时发现解,那么一定有更小的x符合,此时返回真.如果以x为最大值不得不把数列分为超过m段,那么只有当x更大时才可能有解,所以这个问题具有单调性.

    这个函数稍加思考就可以写出很简单的O(n)贪心方法.思想类似于最大子段和问题.

    bool C(long long x){
        int ct = 0;
        long long sum = 0;
        for(int i = 1; i <= n; i++){
            sum += s[i];
            if(sum == x){
                sum = 0;
                ct++;
            }else if(sum > x){
                sum = s[i];
                ct++;
            }
        }
        if(sum) ct++;
        return ct <= m;
    }

    贪心:只要当前段加上下一个数字没有超过m,就把这下一个数字加入当前段,否则需要分出一个新的段.

    证明:对于数列 a, b, c, ... ,当a + b <= m,且a + b + c > m, 那么方案:

    ①|a, b|, |c, ...|, ...(竖线表示分段)

    ②|a|, |b, (c)...|, (|c, ...|)...

    显然地,方案二的分段数量至少比方案①多1.方案①总是更优.


    然后我WA了.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    
    int n, m, s[100010];
    
    bool C(int x){
        int ct = 0;
        long long sum = 0;
        for(int i = 1; i <= n; i++){
            sum += s[i];
            if(sum == x){
                sum = 0;
                ct++;
            }else if(sum > x){
                sum = s[i];
                ct++;
            }
        }
        if(sum) ct++;
        return ct <= m;
    }
    
    int main(){
        cin >> n >> m;
        for(int i = 1; i <= n; i++) scanf("%d", s + i);
    
        int l = 1, r = 1000000000;
        while(l < r){
            int mid = r + l >> 1;
            if(C(mid)) r = mid;
            else l = mid + 1;
        }
    
        printf("%d
    ", l);
    
        return 0;
    }
    WA Code

    初始化条件l = 1是错误的.不论如何,当x小于数列中的最大值时,必定没有解.即解的下限是数列中的最大值.而上述函数C(x)并不会考虑到这一点.

    所以l初始化为数列中的最大值即可.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    
    int n, m, s[100010];
    
    bool C(long long x){
        int ct = 0;
        long long sum = 0;
        for(int i = 1; i <= n; i++){
            sum += s[i];
            if(sum == x){
                sum = 0;
                ct++;
            }else if(sum > x){
                sum = s[i];
                ct++;
            }
        }
        if(sum) ct++;
        return ct <= m;
    }
    
    int main(){
        // freopen("in.txt", "r", stdin);
        cin >> n >> m;
        int l = 0, r = 1000000000;
        for(int i = 1; i <= n; i++) scanf("%d", s + i), l = max(l, s[i]);
    
        while(l < r){
            int mid = r + l >> 1;
            if(C(mid)) r = mid;
            else l = mid + 1;
        }
    
        printf("%d
    ", l);
    
        return 0;
    }
    AC Code
  • 相关阅读:
    Vmware 添加虚拟磁盘
    2019-2020-2 《网络对抗技术》 Exp3 免杀原理与实践
    Docker 容器更换软件源
    Docker 查看容器 Linux 版本
    OpenMediaVault 5 进阶配置(四) Portainer 管理 Docker
    Portainer 中文文档:部署
    树莓派 部署 Docker 数据库容器
    通过Cookie统计上次网页访问时间
    用JavaMail通过QQ邮箱来发送邮件(第一篇博客,备忘)
    删掉双系统
  • 原文地址:https://www.cnblogs.com/Gaomez/p/14171267.html
Copyright © 2011-2022 走看看