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;
 } 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章