- 二维费用的背包问题,第二维费用即为数量。
- 要仔细想想如何打背包,本题中只能是:dp[i][j] 这一格代表 最多花 i 忍耐度,最多打 j 个怪 的限制下,能够获得的最大经验值。另一种想法花给定的经验值获得最少的忍耐度的想法是行不通的,下文会讲。
AC代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 105;
int V,W,n,num;
int w[maxn],v[maxn];
int dp[maxn][maxn] ;
int main(){
while(cin>>V>>W>>n>>num){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=w[i];j<=W;j++){
for(int k=1;k<=num;k++){
dp[j][k] = max(dp[j][k] , dp[j-w[i]][k-1] + v[i]);
}
}
}
int j;
for(j=0;j<=W;j++)
if(dp[j][num] >= V) break;
cout<<W-j<<endl;
}
return 0;
}
- 说一下最后找最小花费的问题,请思考是否可以这样寻找:
int i,j,k;
for(i=1;i<=n;i++){
for(j=w[i];j<=W;j++){
for(k=1;k<=num;k++){
dp[j][k] = max(dp[j][k] , dp[j-w[i]][k-1] + v[i]);
}
if(dp[j][num] >= V)
break;
}
if(dp[j][num] >= V)
break;
}
cout<<W-j<<endl;
- 这样会WA。为什么?因为这样会导致物品还没有看完,就匆忙退出了循环。而很可能需要用到尚未看到的物品,不WA才是奇怪。会产生这样的主意而且没有快速地认识到错误,足见对背包的主要问题还不够敏感。
WA代码:
- 第一,如果要用经验做花费找忍耐,那么经验维必须是恰好,这是为了方便最后我们判断是否达到了W的经验值花费。
- 第二,也是为什么这种做法几乎不可以实现,实际上真正的答案很可能是得到了多于升级所需的经验值,但正常来做只循环到了最大经验值W。那是不是多循环一些格子就可以了呢?也不是,第一,轻松就可以故意造出让你会TLE的数据,你又没办法,只能一点点试,在比赛中罚时直接爆炸。第二,打完背包后找花费值的代码也不算好写,一不留神就会出坑。
#include <iostream>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 105;
int W,V,n,num;
int w[maxn],v[maxn];
int dp[maxn][maxn] ;
int main(){
while(cin>>W>>V>>n>>num){
memset(dp,INF,sizeof(dp));
for(int i=0;i<maxn;i++)
dp[0][i] = 0;
for(int i=1;i<=n;i++)
cin>>w[i]>>v[i];
for(int i=1;i<=n;i++){
for(int j=w[i];j<=W+100;j++){
for(int k=1;k<=num;k++){
dp[j][k] = min(dp[j][k] , dp[j-w[i]][k-1] + v[i]);
}
}
}
int ans = INF;
for(xxxxxxxxxxxxx){
xxxxxxxxx
}
if(V - ans >= 0)
cout<<V-ans<<endl;
else
cout<<-1<<endl;
}
return 0;
}