【2021年CCPC河南省赛】闯关游戏

题目:
小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 
#include
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:"<
		cout<<ans<<endl;
	}
	return 0;
}

你可能感兴趣的:(简单dp,ICPC/CCPC,游戏,动态规划,算法)