P10111 [GESP202312 七级] 纸牌游戏 题解

题目

题面戳我哦~

简单来说就是有三种牌,分别为 \(0\)\(1\)\(2\)\(0\)\(2\)\(1\)\(0\)\(2\)\(1\)。每一轮获胜或平局可以获得不同的分数,拿了一种牌之后不能随意更改,要花一定分数才能去更改,现在要选择一种策略获得最大分数。

分析

比较简单,但有坑,细节较多。

当我们看到求最大分数和对手的牌已经告诉我们的时候,我们很容易想到贪心。不过再仔细想一想,发现不好贪,因为每一步对后面有影响,所以局部最优解不能推出整体最优解,这个时候凭借个人经验就很容易想到用动态规划了。

我们发现有 \(3\) 种牌,所以不难想到用状态机来动态规划,我们定义 dp[i][k][j] 为第 \(i\) 轮打出第 \(k\) 种牌并且一共改变了 \(j\) 次的最大得分。状态转移方程也不难推出是

dp[i][0][j] = max(dp[i-1][0][j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j]))+得分;

dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j] , dp[i-1][2][j-1]-b[j]))+得分;
                
dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j]))+得分;               

另外,我还想说一下几个需要注意的地方:

  1. 看清楚题目,改变牌的费用不是指当前轮次改变费用,是指改变了多少次需要的费用。
  2. 改变牌所需要的费用从 \(1\) 开始存就可以了,没有必要从 \(2\) 存,这样也方便思考,b[i] 就是改变第 \(i\) 次所需要的费用。
  3. 对于第 \(i-1\) 次改变,因为第 \(i-1\) 轮的时候并不能改变 \(i-1\) 次,所以在状态转移的时候不能用这轮不改变来转移,必须由改变一次转移过来。不然相当于前面所有的分数都消失了(因为前面可能会是负分,所以消失了是有影响的)。
  4. 注意在改变 \(0\) 次牌的时候特判,防止数组越界。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1010;

int n;
int a[N] , b[N] , c[N];
int dp[N][3][N];	//dp[i][0][j]表示第i轮当前手牌是0换了j次牌的最大分数,其余同理

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	
	cin >> n;
	for (int i=1 ; i<=n ; i++)
	{
		cin >> a[i];
	}
	for (int i=1 ; i<n ; i++)
	{
		cin >> b[i];
	}
	for (int i=1 ; i<=n ; i++)
	{
		cin >> c[i];
	}
	
	for (int i=1 ; i<=n ; i++)
	{
		for (int j=0 ; j<=i-1 ; j++)
		{
			if (j==0)
			{
				if (c[i]==0)
				{
					dp[i][0][j] = dp[i-1][0][j]+a[i];
					dp[i][1][j] = dp[i-1][1][j]+2*a[i];
					dp[i][2][j] = dp[i-1][2][j];
				}
				else if (c[i]==1)
				{
					dp[i][0][j] = dp[i-1][0][j];
					dp[i][1][j] = dp[i-1][1][j]+a[i];
					dp[i][2][j] = dp[i-1][2][j]+2*a[i];
				}
				else 
				{
					dp[i][0][j] = dp[i-1][0][j]+2*a[i];
					dp[i][1][j] = dp[i-1][1][j];
					dp[i][2][j] = dp[i-1][2][j]+a[i];
				}
			}
			else if (j==i-1)
			{
				if (c[i]==0)
				{
					dp[i][0][j] = max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j])+a[i];
					dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][2][j-1]-b[j])+2*a[i];
					dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][1][j-1]-b[j]);
				}
				else if (c[i]==1)
				{
					dp[i][0][j] = max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j]);
					dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][2][j-1]-b[j])+a[i];
					dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][1][j-1]-b[j])+2*a[i];
				}
				else 
				{
					dp[i][0][j] = max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j])+2*a[i];
					dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][2][j-1]-b[j]);
					dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , dp[i-1][1][j-1]-b[j])+a[i];
				}
			}
			else if (c[i]==0)
			{
				dp[i][0][j] = max(dp[i-1][0][j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j]))+a[i];
				dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j] , dp[i-1][2][j-1]-b[j]))+2*a[i];
				dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j]));
			}
			else if (c[i]==1)
			{
				dp[i][0][j] = max(dp[i-1][0][j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j]));
				dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j] , dp[i-1][2][j-1]-b[j]))+a[i];
				dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j]))+2*a[i];
			}
			else 
			{
				dp[i][0][j] = max(dp[i-1][0][j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j-1]-b[j]))+2*a[i];
				dp[i][1][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j] , dp[i-1][2][j-1]-b[j]));
				dp[i][2][j] = max(dp[i-1][0][j-1]-b[j] , max(dp[i-1][1][j-1]-b[j] , dp[i-1][2][j]))+a[i];
			}
		}
	}
	
	int res = -0x3f3f3f3f;
	for (int i=0 ; i<=n-1 ; i++)
	{
		res = max(max(dp[n][0][i] , res) , max(dp[n][1][i] , dp[n][2][i]));
	}
	cout << res << endl;
	return 0;
}

由于我的码风和没有专门写一个函数去判断得分,所以看起来比较臃肿,但是其实要写的并不多,很多内容复制粘贴就可以。

posted @ 2024-02-22 19:11  hh20080501hh  阅读(25)  评论(0)    收藏  举报