UVA - 714 Copying Book

题目链接:
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

猜你喜欢

转载自blog.csdn.net/zhuixun_/article/details/84501899