Codeforces Round #565 (Div. 3) F.Destroy it!

本文深入解析CodeForces竞赛F题,通过优化01背包问题的算法,采用dp[N][10]策略,有效处理卡牌游戏中的回合制战斗与double攻击特性,实现内存优化与高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

F. Destroy it!

题目地址:http://codeforces.com/contest/1176/problem/F

思路:其实就是一个01背包问题,只是添加了回合和每回合的01限制,和每当已用牌数到了10的倍数,那张卡会触发double攻击。
因为卡使用的多少会触发double效果,所以我们要记录攻击的同时记录卡的使用次数,
可以由01背包dp[N][N]改变,第一个维度是当前是第几个回合,第二个维度是记录卡在用了n张的情况下造成的攻击力,但是dp[N][N](N <= 2e5),占用内存太大显然不行。于是想到用dp[N][10]。
(因为卡的数量可能很多,我们其实只要记录最多三张1费,一张2费,一张3费)
每个回合用卡情况最多3种:
1.用3张一费卡。
2.用2张一费卡,或者1张一费卡,1张二费卡
3.用1张一费卡,或者1张二费卡,或者1张三费卡

#include<iostream>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(int i = (j); i <= (k); i++)
#define per(i,j,k) for(int i = (j); i >= (k); i--)
#define mod(x) ((x)%(MOD))

typedef long long LL;
const int MOD = 10;
const int N = 2e5 + 10;
LL dp[N + 2][10];

void init(){
	rep(i, 0, N) rep(j, 0, 9) dp[i][j] = -1;
}

int main(){

	ios::sync_with_stdio(false);
	cin.tie(0);

	init();

	int n;
	cin >> n;
	
	dp[0][0] = 0;//初始化
	
	rep(o, 1, n){
		int num;
		cin >> num;

		int L1 = 0;
		int L2 = 0;
		int L3 = 0;
		int c1[5] = { 0 };//费用1,记录v最大的三张
		int c2 = 0;//费用2
		int c3 = 0;//费用3

		int c, v;
		rep(i, 1, num){
			cin >> c >> v;

			if (c == 1){
				if (L1 == 3){
					int x = 1;
					if (c1[x] > c1[2]) x = 2;
					if (c1[x] > c1[3]) x = 3;
					if (v > c1[x]) c1[x] = v;
				}
				else c1[++L1] = v;
			}
			else if(c == 2){
				L2=1;
				if (v > c2) c2 = v;
			}
			else if (c == 3){
				L3=1;
				if (v > c3) c3 = v;
			}
		}

		sort(c1 + 1, c1 + 1 + 3);

		int max_v = max(c1[3], max(c2, c3));//一张v最大的
		int _2_1 = c1[3];//两张卡费用最大的
		int _2_2 = c1[2];
		if (c2 > _2_2) _2_2 = c2;
		if (_2_2 > _2_1) swap(_2_2, _2_1);//两张卡,由1费中间v最大的2张,和一张2费的中间选出v最大的两张
		LL _3 = c1[1] + c1[2] + c1[3];//用三张1费

		rep(i, 0, 9) dp[o][i] = dp[o - 1][i];//先把上个回合的状态保存到当前回合,方便下边的比较
		//因为这三种状态都是附加在上个状态下得出的,所以他们之间都是独立的。
		//mod(i + x),(i + x >= 10)* value 用于判断是否满足大于等于用的次数是十张的倍数
		rep(i, 0, 9){
			if (dp[o - 1][i] != -1){//上个状态是存在的
				if (L1 + L2 + L3 >= 1){//用的卡牌最少一张的时候,用一张情况
					dp[o][mod(i + 1)] = max(dp[o][mod(i + 1)], dp[o - 1][i] + max_v + (i + 1 >= 10)*max_v);
				}
				if (L2 + L1 >= 2){//1费2费用的卡牌至少有两张,用两张情况
					dp[o][mod(i + 2)] = max(dp[o][mod(i + 2)], dp[o - 1][i] + _2_1 + _2_2 + (i + 2 >= 10)*_2_1);
				}
				if (L1 >= 3){//1费卡牌数最少三张,用三张情况
					dp[o][mod(i + 3)] = max(dp[o][mod(i + 3)], dp[o - 1][i] + _3 + (i + 3 >= 10)*c1[3]);
				}
			}
		}
	} 
	
	LL ans = -1;

	//rep(i, 0, n){
	//	rep(o, 0, 9) cout << dp[i][o] << " ";
	//	cout << endl;
	//}
	//cout << endl;

	rep(o, 0, 9) ans = max(dp[n][o], ans);

	cout << ans << endl;

	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值