UVA - 10163 Storage Keepers

开始并没有注意到需要使两个量最优与只要求一个量最优有什么不同,就定义dp[i][j]为前i个人看守j个storage的最大安全线,然后顺便把wage求出来。交上去却一直wa。疑惑不解的去看别人的代码,郁闷的发现所有人都用了两次dp,我开始非常不解,既然薪水是由人决定的为什么还要再用一次dp?后来仔细想想才发现这样做的道理,从较高的抽象层面看,定义的状态dp[i][j]本身就是求一个量的最优值的,而不是两个量。从代码具体实现看,对于薪水,由于遍历i的时候,i小的总是在i大的之前访问,就有可能导致较小的i添加进去能够导致安全线的最优解但不是薪水最优解,且添加进去之后无法移除,导致之后的状态无法达到最优解。也就是安全线是一样的,但薪水是可能达不到最优解得。所以,对于两个量,必须用两个dp。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define MAXN 105
#define MAXM 35
#define INF 200000
using namespace std;

int s[MAXM],n,m,dp[MAXM][MAXN],wage[MAXM][MAXN];

void init(){
	memset(wage,0,sizeof(wage));
	memset(dp,0,sizeof(dp));
	for(int i=0;i<=m;i++)
		dp[i][0]=INF;
}

int main(){
	while(~scanf("%d %d",&n,&m)&&n+m){
		for(int i=1;i<=m;i++){
			scanf("%d",&s[i]);
		}
		sort(s+1,s+m+1);
		init();
		for(int i=1;i<=m;i++){
			for(int j=0;j<=n;j++){
				for(int t=1;t<=j;t++){
					if(dp[i-1][j-t]>0&&s[i]/t>0){
						if(min(dp[i-1][j-t],s[i]/t)>dp[i][j]){
							dp[i][j]=min(dp[i-1][j-t],s[i]/t);
							wage[i][j]=wage[i-1][j-t]+s[i];
						}
						else if(min(dp[i-1][j-t],s[i]/t)==dp[i][j]){
							wage[i][j]=min(wage[i][j],wage[i-1][j-t]+s[i]);
						}
					}
				}
				if(j!=0&&dp[i-1][j]>dp[i][j]){
					dp[i][j]=dp[i-1][j];
					wage[i][j]=wage[i-1][j];
				}
				else if(j!=0&&dp[i-1][j]==dp[i][j]&&dp[i][j]!=0)
					wage[i][j]=min(wage[i][j],wage[i-1][j]);
			}
		}
		int safe=dp[m][n];
		if(safe==0){
			cout<<"0 0"<<endl;
			continue;
		}
		memset(dp,0x7f,sizeof(dp));
		dp[0][0]=0;
		for(int i=1;i<=m;i++){
			for(int j=0;j<=n;j++){
				for(int t=1;s[i]/t>=safe&&t<=j;t++){
					dp[i][j]=min(dp[i][j],dp[i-1][j-t]+s[i]);
				}
				dp[i][j]=min(dp[i][j],dp[i-1][j]);
			}
		}
		cout<<safe<<' '<<dp[m][n]<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值