题目:
小i正在玩一个闯关游戏,游戏一共n关。
初始的时候小i有H点体力以及0个金币。
小i只能按从第1关到第n关按顺序完成。在第i关时,小i要在三种操作中选择一种:
1.当前体力不小于Ai
可以选择这个操作,消耗Ai
点体力,获得Bi
个金币。
2.当前体力不小于Ci
可以选择这个操作,消耗Ci
点体力,获得Di
个金币。
3.结束游戏,直接结算。
当小i完成全部n个关卡后会自动结束游戏,进行结算。
结算时小i最多获得了多少金币?
题解:
如果没有条件3就是很明显的背包dp。但在3的条件下,可以发现原先背包转移所含的“拿”与“不拿”变成了必须在此环节拿一个,那么转移条件就应该变为:
i表示第i组,j表示体力。用-1表示此处没走的情况。
int k=-1;
if(j>=a[i]&&dp[i-1][j-a[i]]>=0)k=dp[i-1][j-a[i]]+b[i];
if(j>=c[i]&&dp[i-1][j-c[i]]>=0)k=max(k,dp[i-1][j-c[i]]+d[i]);
dp[i][j]=k;
j>=a[i]:保证体力足够
dp[i-1][j-a[i]]>=0:保证上一步不为空。
虽然但是,这么交上去空间会炸。
然后我学到了一个新东西:滚动数组。
真的秒哇~直接简到dp[2][maxn]!因为其实每次dp只和上一步有关,故只记录上一步的数据。通过&1来实现。
学习滚动数组具体见:《滚动数组》—滚动数组思想,运用在动态规划当中
全部代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
const int maxn=6000;
int a[maxn],b[maxn],c[maxn],d[maxn];
#define ll long long
int dp[2][maxn];
int main() {
int t;cin>>t;
while(t--){
int n,h;cin>>n>>h;
for(int i=1;i<=n;i++){
sc("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
}
//dp:滚动数组!
int ans=-1;//记录最大值
for(int i=0;i<h;i++){
dp[0][i]=-1;
dp[1][i]=-1;
}
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=h;j++){
int k=-1;
if(j>=a[i]&&dp[i-1&1][j-a[i]]>=0)k=dp[i-1&1][j-a[i]]+b[i];
if(j>=c[i]&&dp[i-1&1][j-c[i]]>=0)k=max(k,dp[i-1&1][j-c[i]]+d[i]);
dp[i&1][j]=k;
ans=max(ans,k);
}
}
//cout<<"ans:"<<endl;
cout<<ans<<endl;
}
return 0;
}