UVA 714 Coping Books 二分+贪心

二分查找最小的最大值,然后贪心划分区间

题解:x为划分的各个区间之和的最大值,那么x在[min,max]中,min为序列中的最小值(0也行,用最小值可以稍微减少二分的时间,),max为序列之和。

函数P(x)表示当子序列最大值为x时,能至少划分出的区间个数,即每次尽量向右划。如果划完整个序列之后区间数<=K,那么x一定大于等于答案,如果区间数大于K,那么x一定取偏大了。不同情况进行二分,直到找到最小的x,使得划分的区间数<=K(想一下为什么不是等于k,而是也有等于的时候)

找到最小的最大值ans后,由于题目中要求有多解是前面的区间尽量小,所以我们从右边开始划分,每次尽量往左划。

最后将没用到的‘/’放到最靠右边能分隔的位置。

注意使用 long long

#include<bits/stdc++.h>
using namespace std;
long long  a[505],m,k;
set<int>ans;
long long Min,Max;
bool P(long long x){
	long long sum=0,flag=0;
	for(int i=0;i<m;i++){
		if(sum+a[i]<=x){sum+=a[i];}
		else{
			flag++;
			if(flag>=k) return false;
			sum=a[i];
		}
	}
	
	return true;
}

int solve(long long  l,long long r){
	long long mid=(l+r)/2;
	if(l==r) return l;
	if(P(mid)){
		return solve(l,mid);
	}
	else{
		return solve(mid+1,r);
	}
}
int main()
{
	int t;
	
	cin>>t;
	while(t--){
		Max=Min=0;
		ans.clear();
		cin>>m>>k;
		for(int i=0;i<m;i++){
			scanf("%d",&a[i]);
			Min=max(Min,a[i]);
			Max+=a[i];
		}
		
		int p=solve(Min,Max);
		int sum=0,flag=0;
		for(int i=m-1;i>=0;i--){
			if(sum+a[i]<=p) {sum+=a[i];}
			else{
				ans.insert(i+1);
				flag++;
				sum=a[i];
			}
		}
		int beg=1;
		while(flag<k-1){
			if(ans.count(beg)){
				beg++;
				continue;
			} 
			else {
				ans.insert(beg);
				beg++;flag++;
			}
		}
		
		printf("%d",a[0]);
		for(int i=1,j=0;i<m;i++){
			if(ans.count(i)){
				printf(" /");
				ans.erase(i);	
			}
			printf(" %d",a[i]);
		}
		printf("\n");
	}
	return 0;
 } 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值