开始并没有注意到需要使两个量最优与只要求一个量最优有什么不同,就定义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;
}