题目链接:
UVA - 714 Copying Book
题意:
给定一个m个数的序列,将这个序列分成k份连续子序列,要求子序列的最大值最小。
思路:
二分、贪心
这段序列不用再排序,先求出这段序列的和以及序列的最大值,然后从序列的最大值到序列的和开始二分查找,找到划分段数小于等于k的最小上限。
然后根据上面找到的上限,从最后开始往前划分,因为前面找到的划分段数可能小于k,这时候把原序列前面部分每个值划分成一段,使最后的划分数=k。
注意:
读入的序列a[505],以及过程中的求和,区间等值记得开long long.
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll a[505],vis[505];
int main(int argc, char** argv) {
int Case;
scanf("%d",&Case);
while(Case--){
int m,k;
scanf("%d %d",&m,&k);
ll minv=-1;
ll total=0;//5*10^9
for(int i=1;i<=m;i++){
scanf("%lld",&a[i]);
if(a[i]>minv) minv=a[i];
total+=a[i];
}
ll l=minv,r=total;
while(l<r){
ll mid=(l+r)>>1;
ll num=1,sum=0,flag=0;//初始化块的大小很关键,num=1
for(int i=m;i>=1;i--){//从后往前划分,使前面剩下的数尽量小
if(sum+a[i]>mid){
num++;
sum=a[i];
if(num>k){
flag=1;
l=mid+1;
break;
}
}else{
sum+=a[i];
}
}
if(!flag) r=mid;
}
ll sum=0,num=1;
memset(vis,0,sizeof(vis));
for(int i=m;i>=1;i--){
if(sum+a[i]>l){
vis[i]=1;
sum=a[i];
num++;
}else{
sum+=a[i];
}
if(k-num==i){//不够分
for(int j=1;j<=i;j++){
vis[j]=1;
}
break;
}
}
for(int i=1;i<=m;i++){
if(i==1) printf("%d",a[1]);
else printf(" %d",a[i]);
if(vis[i]) printf(" /");
}
printf("\n");
}
return 0;
}
思路参考:
http://www.cnblogs.com/g0feng/archive/2012/10/16/2726726.html