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\) 存,这样也方便思考,
b[i]就是改变第 \(i\) 次所需要的费用。 - 对于第 \(i-1\) 次改变,因为第 \(i-1\) 轮的时候并不能改变 \(i-1\) 次,所以在状态转移的时候不能用这轮不改变来转移,必须由改变一次转移过来。不然相当于前面所有的分数都消失了(因为前面可能会是负分,所以消失了是有影响的)。
- 注意在改变 \(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;
}
由于我的码风和没有专门写一个函数去判断得分,所以看起来比较臃肿,但是其实要写的并不多,很多内容复制粘贴就可以。

浙公网安备 33010602011771号