hdu4597 Play Game 区间DP

时间:2023-03-08 21:41:59

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4597

全国邀请赛通化赛区第8题--题目重现

思路:

区间DP的思想,想法是队友想出来的,感觉很秒,自己处理的边界,果断AC

边界处理很重要!!

对于两列牌。我们定义f[x][y][k][h]表示对于当前状态中第一列牌处于区间(i,j),第二列牌处于区间(k,h)时,先手(即当前要选择牌的选手)和后手之间的最大差值

定义sum1[x][y][k][h]表示先手的在此区间的获得的分数的最大值,sum2[x][y][k][h]表示后手在此区间的获得的分数的最大值

那么如果对于当前的区间,当前的选手选择y位置的牌,那么f[x][y][k][h]=(sum1[x][y-1][k][h]+a[y]-sum2[x][y-1][k][h]);

又很容易知道sum1[x][y-1][k][h]-sum2[x][y-1][k][h]=-f[x][y-1][k][h];

为什么是负的呢??很简单,因为上一次的先手是对方啊

同理,也可以选择x,k,h位置的牌

那么我们最终可以得到这样一个简单的DP转移方程

f[x][y][k][h]=max(-dfs(x,y-1,k,h)+a[y],-dfs(x+1,y,k,h)+a[x],-dfs(x,y,k+1,h)+b[k],-dfs(x,y,k,h-1)+b[h]);

状态方程有了,实现的时候最重要的就是边界处理了。

我用的是记忆化搜索的方式

代码:

 #include <stdio.h>
#include <string.h>
#include <algorithm>
#include<iostream>
using namespace std;
#define N 22
#define INF 999999999
int f[N][N][N][N], a[N], b[N], sum[][N];
bool vis[N][N][N][N];
int n;
int dfs(int i, int j, int k , int h)
{
int &x = f[i][j][k][h];
if(vis[i][j][k][h]) return x;
vis[i][j][k][h] = true;
x=-INF;
if(k==&&h==&&i==&&j==) return x=;
else
if(k==&&h== && i==j) return x=a[i];
else
if(i==&&j== && k==h) return x=b[k];
else
if(k==&&h==) x=max(max(-dfs(i+,j,,)+a[i],-dfs(i,j-,,)+a[j]),x);
else
if(i==&&j==) x=max(max(-dfs(,,k+,h)+b[k],-dfs(,,k,h-)+b[h]),x);
else
if(i==j && k!=h) x=max(max(max(-dfs(,,k,h)+a[i],-dfs(i,j,k+,h)+b[k]),-dfs(i,j,k,h-)+b[h]),x);
else
if(k==h && i!=j) x=max(max(max(-dfs(i,j,,)+b[k],-dfs(i+,j,k,h)+a[i]),-dfs(i,j-,k,h)+a[j]),x);
else
if(i==j&& k==h) x=max(x,max(-dfs(,,k,h)+a[i],-dfs(i,j,,)+b[k]));
else
{
x= max(x, max(-dfs(i, j-, k, h) + a[j], -dfs(i+, j, k, h) + a[i]));
x = max(x, max(-dfs(i, j, k, h-) + b[h], -dfs(i, j, k+, h) + b[k]));
} return x;
}
int main()
{
int cases;
scanf("%d", &cases);
while(cases--) {
scanf("%d", &n);
int sum=;
for(int i = ; i <= n; i++) scanf("%d", &a[i]),sum+=a[i];
for(int i = ; i <= n; i++) scanf("%d", &b[i]),sum+=b[i];
memset(vis, false, sizeof(vis));
int res = dfs(, n, , n);
cout<<(sum+res)/<<endl;
}
return ;
}