题目描述:
两个人在DAG上的博弈,图上有两个旗子,一白一黑,每次可以移动一个旗子,不能移动的那个人为负。输的人要负相应的罚金。移动一次白旗罚金增加,移动黑旗罚金减少。问最后先手获得最多的钱和第一步有多少种走法。
大致思路:
定义状态dp(i,j,k)表示白旗在i,黑旗在j,当前罚金为k时候能得到的最大钱数,转移可以转移到i指向的边,和j指向的边,最后i和j都没有出边的时候返回。
代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int maxn = 50 + 10;
const int inf = 65536;
vector<int> g[maxn];
int p[maxn];
int n,m,x,y;
int dp[maxn][maxn][maxn*4];
int sum[maxn][maxn][maxn*4];
int rec(int x,int y,int k) {
if (sum[x][y][k+100] != -1) return dp[x][y][k+100];
int &a = dp[x][y][k+100],&b = sum[x][y][k+100];
a = (g[x].empty() && g[y].empty()) ? -k : -inf;
b = 1;
for (int i = 0; i < g[x].size(); i++) {
int tmp = -rec(g[x][i],y,k+p[g[x][i]]);
if (tmp > a) {
a = tmp;
b = 1;
}
else if (tmp == a) b++;
}
for (int i = 0; i < g[y].size(); i++) {
int tmp = -rec(x,g[y][i],k-p[g[y][i]]);
if (tmp > a) {
a = tmp;
b = 1;
}
else if (tmp == a) b++;
}
return a;
}
int main() {
while (cin>>n>>m>>x>>y) {
//memset(dp,inf,sizeof(dp));
memset(sum,0xff,sizeof(sum));
for (int i = 0; i < n; i++) {
scanf("%d",p+i);
g[i].clear();
}
for (int i = 0; i < m; i++) {
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
}
rec(x,y,1);
cout<<dp[x][y][101]<<" "<<sum[x][y][101]<<endl;
}
}