uva 714-Copying Books 二分最小化最大值+双贪心!+输出路径

此题只比poj 3273多了个输出路径,Judge函数里面更巧妙:

①最小化最大值区间是(l,r],最大化最小值区间是[l,r),判断条件一个upper_bound,一个是lower_bound,细细体会。

②第一次for找出合适的最大区间和,然后反着找,划分区间(贪心,可以让前面区间和尽量小),最后如果有剩余未划线的,顺着一个一个划线。(贪心,单独一个数成一个区间和最小)

③计数问题,特别求和、移位,注意溢出问题,如此题求和5x10^9可能爆int。


很巧妙!用心体会!


#include <iostream>

#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll a[5005];
int vis[5005];
int n,k;


bool Judge(ll mid)//最大值为mid ,各个temp<=mid,count>=m 
{
ll temp=0;
int count=1;

for(int i=0;i<n;i++)
{
if(temp+a[i]<=mid)
{

temp+=a[i];

}
else
{
temp=a[i];
count++;//不满足 则新开一组 而不是到组尾再加一 


}


}
//cout<<mid<<"de zu"<<count<<endl;
if(count>k)//最小化最大值用>号
return true;
else         // 就算等于,还想找和更小的(组数更大的) ,所以往左找
return false;
}




int main()
{
int T;
scanf("%d",&T);




while(T--)
{
ll sum=0;
ll max1=0; 
scanf("%d%d",&n,&k);
    memset(vis,0,sizeof(vis));
    //memset(vis2,0,sizeof(vis2));
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
max1=max(max1,a[i]);
sum+=a[i];
}

ll l=max1;//注意这里要用long long,不然会WA 
ll r=sum;
ll mid;
while(l<=r)
{
mid=(l+r)/2;
if(Judge(mid))
l=mid+1;
else
r=mid-1;
}
//cout<<l<<endl;
ll temp2=0;
int sheng=k-1; //目前剩下要划线的数量 
for(int i=n-1;i>=0;i--)//倒回去找,很巧妙,细细体会 
{


if(temp2+a[i]<=l)//我这个写法是选左端点l
{
temp2+=a[i];
}
else
{
vis[i]=1;
temp2=a[i];
sheng--;//此处划开 
}



for(int i=0;i<n;i++)//顺回去补足 
{
cout<<a[i];
if(i<n-1)
cout<<" ";
if(vis[i]==1||sheng>0)
{
cout<<"/ " ;
if(vis[i]==0)//这里用到贪心,题目要求前面组的和尽可能小,所以前面尽可能都是单个数一组 
sheng--;
}
}

cout<<endl;

}
}

猜你喜欢

转载自blog.csdn.net/zjyang12345/article/details/80285712