逆向背包问题 洛谷P1802
看到这道题的时候我觉得大家一定会觉得这是一个很基础的背包模板题,但上手了之后感觉不太对劲,再读题就发现这个题的解题思路有点“逆"。
作为动态规划的新手,逆的背包也让我想了很久
下面先放出本题AC代码:
#include<bits/stdc++.h>
using namespace std;
int win[1005],lose[1005],use[1005];
long long v[1005][10000];
int main()
{
int n,x;
cin>>n>>x;
for(int i=1;i<=n;i++)
cin>>lose[i]>>win[i]>>use[i];
for(int i=1;i<=n;i++)
{
for(int j=0;j<=x;j++)
{
if(j>=use[i]&&v[i-1][j-use[i]]+win[i]>v[i-1][j]+lose[i])
{
v[i][j]=v[i-1][j-use[i]]+win[i];
}
else v[i][j]=v[i-1][j]+lose[i];
}
}
cout<<v[n][x]*5<<endl;
return 0;
}
我们先分析一波题目:
题目的意思用我的话来说就是:给你 x 钱,打败一个人花 use[i] 钱,无论打败与否都会给出一定的奖励,问在资金缺乏的情况下,如何选择打败对象使得奖励值最大。
现在将此题与传统背包问题做出对比:
解决传统背包问题的思路是:设背包的剩余容积为 x 求在这个剩余容积下的背包价值最大情况,所以我们需要遍历所有物品,每个物品考虑放与不放的价值。
现在把此题转化为背包问题来思考:此题应该要找 当你的敌人是第i个时, 还有j瓶药水的时候,奖励最大可能为多少,所以进行对比的参照物应该是 当你的敌人是第 i-1个时,还有j-use[i]瓶药水的时候,奖励最大值为多少。
这里很关键,仔细想想,为什么是j-use[i]瓶药水。
这样考虑,应为药水是用一点少一点的,那么我们做出的假设有两个,一个是:使用use[i]瓶药水打败这个敌人。另一个是:一瓶都不用,直接认输,将损失将到最小。
所以为了使假设一能够成立,我是不是要保证一定有足够的药水给我去打败这个敌人?
而我要找的是当我一共有 j 瓶药水时,我能得到的最大奖励。
所以如果又要实现假设一,又要满足这个前提,代码就要这样写:
1.为保证假设一 提前把use[i]提取出来 [j-use[i]]
2.为了找最大,就要找药水数为 j-use[i] 的最大,所以 v[i-1][j-use[i]]。
注意这一点后就是常规的比较了。
此题还有一点比较有趣,我第一次交WA了一半数据,看了数据后发现:打败一个敌人可以一瓶药水也不用,所以在写药水总共多少的时候应该从0开始,即如果一瓶药水也没有,也有可能打败一些敌人。