UVa 714 抄书 二分答案

来源:互联网 发布:3ds主机sd卡容量算法 编辑:程序博客网 时间:2024/04/29 21:58

题意:
把一个包含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,不如刚才的好。每个 整数不超过107。如果有多解,S(1)应尽量小。如果仍然有多解,S(2)应尽量小,依此类推。
分析:
二分最大值,用O(n)扫一遍判断,T=O(nlogn)。
打印最优解的时候比较麻烦,我WA了几次,扫后面尽可能取多的数,然后因为要有k个划分,也就是要k-1个’\’,所以处理到还剩下k个数的时候每隔一个放一个’\’。(注意下标从0开始)

#include<cstdio>#include<stack>#include<iostream>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int N=555;int a[N];int n,k;bool ok(ll x){    int tot=k-1;    ll t=0;    for(int i=0;i<n;i++){        t+=a[i];        if(t>x){            i--; tot--; t=0;            if(tot<0)return 0;        }    }    return 1;}int main(){    int T; scanf("%d",&T);    while(T--){        scanf("%d%d",&n,&k);        for(int i=0;i<n;i++)scanf("%d",&a[i]);        if(k==1){            for(int i=0;i<n-1;i++)printf("%d ",a[i]);            printf("%d\n",a[n-1]);continue;        }        ll l=0,r=5000000000;        while(l<r){            ll m=l+(r-l)/2;            if(ok(m))r=m;            else l=m+1;        }        stack<int>s;        ll t=0;        for(int i=n-1;i>=0;i--){            t+=a[i];            if(i+1==k-1)s.push(i),k--; //还剩下i+1个数,最多可以划分成k个,那么就是放k-1个'/'            else if(t>l){                s.push(i);                i++; k--;                t=0;            }        }        int i;        for(i=0;i<n-1;i++){            printf("%d ",a[i]);            if(i==s.top()){                s.pop();                printf("/ ");            }            if(s.empty())break;        }        for(i=i+1;i<n-1;i++)printf("%d ",a[i]);        printf("%d\n",a[n-1]);    }    return 0;}
0 0