UVa:714 Copying Books .

 

最大值最小化的问题,卡了两天才做出来。


入门经典上有分析,但是只提供了求最小的最大值的思路。
具体方法是在某区间上用二分法猜数字,最后猜的一个数x是使得【将输入数列划分成m个连续子序列使得所有S(i)均不超过x】成立的最小x,这就是最小的最大值。


这里二分的并非数组,而是一种很抽象的形式。还要好好领会。


二分的区间自然是数列的最大值到数列总和。

 

之后的如何切分是让我最困惑的地方。

从网上查了下是用贪心的思路,这是为了保证在多种解的情况下,输出第一个人工作量最小的情况。


从后往前划分,使得每段区间小于等于之前所求的最小最大值。这样划分完了如果恰好是k块那么直接输出。如果不足k块再从前往后依次划分补足k块。

 

令我不解的地方是如果恰好第一个人的工作量是最大值呢,此时如果不足k块,那么这样划分岂不是将第一个人的工作分割了吗。最后就不存在与之相等的最大值了。

可是这样做AC了。那么很明显不存在我想的那种情况,可是为什么呢?

 

这里至今没有想清楚,求大神指教。

 

还有一点是其中数据要用long long ,在这里WA了一次。

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
int a[505]={0};
int b[505]={0};
bool fun(long long x)
{
    long long sum=0;
    int N=0,i=0;
    for(i=0;i<n;++i)
    {
        if(sum+a[i]<=x){sum+=a[i];}
        else if(i==0&&sum+a[i]>=x) return false;
        else if(sum+a[i]>=x)
        {
            N++;
            sum=0;
            if(N==k-1) break;
            sum=a[i];
        }
    }
    for(;i<n;i++)
    sum+=a[i];
    if(sum<=x) return true;
    else return false;
}
long long Bsearch(long long max,long long sum)
{
    long long x=max,y=sum;
    while(x<y)
    {
        long long mid=(y+x)/2;
        if(fun(mid)) y=mid;
        else x=mid+1;
    }
    return x;
}
int main()
{

   // freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%d%d",&n,&k);
        long long mx=0,sum=0;
        for(int i=0;i<n;++i)
        {
            scanf("%d",&a[i]);
            mx=(mx>a[i])?mx:a[i];
            sum+=a[i];
        }
        long long val=Bsearch(mx,sum);
        int nn=0;
        sum=0;
        for(int i=n-1;i>=0;--i)
        {
            if(sum+a[i]<=val) sum+=a[i];
            else if(sum+a[i]>val)
            {
                b[i]=1;nn++;
                sum=a[i];
            }
            if(nn==k-1) break;
        }
        if(nn<k-1)
        {
            for(int i=0;i<n&&nn!=k-1;++i)
            if(!b[i]){b[i]=1;nn++;}
        }
        for(int i=0;i<n;++i)
        {
            printf("%d",a[i]);
            if(i==n-1) printf("\n");
            else if(b[i]) printf(" / ");
            else  printf(" ");
        }
    }
    return 0;
}

 


 

 

你可能感兴趣的:(二分法,贪心法)