Codevs 抄书问题1&2&3

对于抄书问题1,由于标签上写的DP,我就写了DP。。。。
设dp[I][j]表示前i本书由j个人抄的最小答案,则状态转移方程为
dp[I][j]=min{max(dp[k][j-1],s[I]-s[k])},其中s[]为前缀和,k从j-1到1枚举。
输出方案时,由于题目要求多解时使前面的人抄的尽量少,因此可以贪心地输出。
由于我们在前面的DP过程后已经知道了每个人抄书的页数的最大值的最小值dp[m][k],因此可以逆序枚举第i个人,将不超过dp[m][k]的书都交给i抄,然后得出的答案保证前边的人抄书尽可能少,这样就能AC抄书问题1了。时间复杂度
O(k*m^2+k);
#include
#define maxn 105
#define inf 0x7fffff
using namespace std;
int m,k,book[maxn],s[maxn];
struct Anses{
	int from,to;
};
int dp[maxn][maxn];
int main(){
	ios::sync_with_stdio(false);
	cin>>m>>k;
	for(int i=1;i<=m;i++){
		cin>>book[i];
		s[i]=s[i-1]+book[i];
		dp[i][1]=s[i];
	}
	for(int i=2;i<=m;i++){
		for(int j=2;j<=k&&j=1;k--){
				int maxx=0;
				if(i-kmaxx) minx=maxx;
			}
			dp[i][j]=minx;
		}
	}
	int ansxx=dp[m][k];
	Anses ans00[maxn];
	int i=m,k0=k;
	while(k0){
		int j=i-1;
		while(s[i]-s[j]=0) j--;
		if(s[i]-s[j]==ansxx){
			ans00[k0].from=j+1;
			ans00[k0].to=i;
			k0--;
			i=j;
		}
		else if(s[i]-s[j+1]<=ansxx){
			ans00[k0].from=j+2;
			ans00[k0].to=i;
			k0--;
			i=j+1;
		}
	}
	for(int i=1;i<=k;i++)cout<
但是对于抄书问题2&3,以上DP显然不行,因此得换思路。
刚才的讨论中,DP过程的作用,是求出m本书分成k份的最大代价的最小值,之后贪心地输出就可以了,是不是有二分答案的味道?
我们可以二分查找最大价值的最小值,然后用刚刚所得的贪心策略验证,如果分给了>k个人,说明二分的答案偏小,反之偏大。
但是codevs的数据神坑。。。。。
由于我们刚才的贪心策略,有可能根本用不了K个人就能在最优策略下抄完m本书,此时程序将前边的人分配了0本书!但是根据codevs上的数据,应该是每个人都至少抄一本书,呵呵。。。。
#include
#define maxn 1000000+5
using namespace std;
int s[maxn],n,k,begin[maxn],end[maxn]; 
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>k;
	if(!k)return 0;
	for(int i=1;i<=n;i++){
		cin>>s[i];
		s[i]+=s[i-1];
	}
	int lf=1,ri=s[n],ans,mid;
	for(int i=1;i<=k;i++)begin[i]=end[i]=i;
	while(lf>1;
		int b=n,e=n,cnt=0;
		while(b>=0){
			if(s[b]-s[b-1]>mid){
				lf=mid+1;
				goto drg;
			}
			if(s[e]-s[b-1]<=mid) b--;
			else{cnt++;e=b;}
		}
		cnt++;
		if(cnt>k) lf=mid+1;
		else ri=mid;
	}
	int b=n,e=n,mark=0;
	for(int i=k;i>=1;i--){
		b=e;
		while(s[e]-s[b-1]<=ri){
			if(b==i){
				end[i]=e;
				for(int i=1;i<=k;i++)cout<


你可能感兴趣的:(贪心)