【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 <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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值