Description
CZL发明了一种叫作舳舻牌的双人纸牌游戏,据说具有提神醒脑,延年益寿的功效。这次,CZL和他的对手YYY进行游戏,CZL先手。
首先,桌子上平铺着N张牌,从1至N标号。每张牌都有一个收益值,可正可负,收益值用Wi表示。每张牌对每个人都有一个诱惑值,与收益值无关。
游戏开始时,CZL先手,两人交替进行游戏。轮到某个人时,ta报出一个整数X,然后拿走桌上剩余的牌中所有诱惑值小于等于X的牌(至少拿一张)。当桌上没有牌时,游戏结束。收益值总和高者获胜。
我们对CZL和YYY的智商还是不怀疑的,所以可以保证他们都是按照最优策略操作,使自己的分数尽量多。请你编程求出CZL最终的收益值之和。
Input
第一行一个整数N,表示一开始桌子上的牌的数目。接下来一行N个整数,表示每张牌的收益值,接下来N行每行两个数字,分别表示这张牌对CZL和YYY的诱惑值。
Output
一行一个数字M,表示CZL最后的收益值。
Sample Input
5
-4 2 -4 1 -300
1 2
2 2
3 1
5 5
1 4
Sample Output
-302
Data Constraint
对于30%的数据,N≤20。
对于100%的数据 N≤1000,|Wi|≤10^9,诱惑值是int范围内的整数。
Solution
可以算是我的第一道博弈论的题目,用来上上手。
可以考虑倒着来做,设
f
i
,
j
f_{i,j}
fi,j表示当前CZL报了
i
i
i这个数,YYY报的是
j
j
j这个数时,CZL为先手,CZL能取到的最多的价值。
在设一个
g
i
,
j
g_{i,j}
gi,j表示YYY当前报的是
j
j
j这个数,CZL报了
i
i
i这个数,YYY为先手,YYY能取到的最多的价值。
f i , j = m a x ( f i , j , s u m i , j − g k , j ) f_{i,j}=max(f_{i,j},sum_{i,j}-g_{k,j}) fi,j=max(fi,j,sumi,j−gk,j)
g i , j = m a x ( g i , j , s u m i , j − f k , j ) g_{i,j}=max(g_{i,j},sum_{i,j}-f_{k,j}) gi,j=max(gi,j,sumi,j−fk,j)
其中
s
u
m
i
,
j
sum_{i,j}
sumi,j表示的是取了i或j以上总共能够取到的价值。
转移式的意思就是用总的价值减去对方的价值。
不过有一些特殊的情况,就是当前的i或j是没有这一个数的,就直接用上一个转移过来,不用减去后面的
g
k
,
j
g_{k,j}
gk,j和
f
k
,
j
f_{k,j}
fk,j。
Code
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
#define ll long long
using namespace std;
const int N=1e3+5;
const ll INF=1e17;
int n,cnt,cnt1;
int b[N],c[N],d[N],e[N];
map <ll,int> ma,ma1;
ll a[N],t[N][N],sum[N][N],F[N],G[N],f[N][N],g[N][N];
int main() {
freopen("poker.in","r",stdin);
freopen("poker.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++) {
scanf("%d%d",&b[i],&c[i]);
d[i]=b[i];e[i]=c[i];
}
sort(d+1,d+1+n);
for(int i=1;i<=n;i++)
if(!ma[d[i]])ma[d[i]]=++cnt;
sort(e+1,e+1+n);
for(int i=1;i<=n;i++)
if(!ma1[e[i]])ma1[e[i]]=++cnt1;
for(int i=1;i<=n;i++) {
b[i]=ma[b[i]];c[i]=ma1[c[i]];
sum[b[i]][c[i]]+=a[i];
t[b[i]][c[i]]++;
}
for(int i=n;i;i--)
for(int j=n;j;j--) {
sum[i][j]=sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1]+sum[i][j];
t[i][j]=t[i+1][j]+t[i][j+1]-t[i+1][j+1]+t[i][j];
}
for(int i=n;i;i--)
for(int j=n;j;j--) {
if(t[i][j]==t[i+1][j])f[i][j]=f[i+1][j];
else f[i][j]=sum[i][j]-F[j];
if(t[i][j]==t[i][j+1])g[i][j]=g[i][j+1];
else g[i][j]=sum[i][j]-G[i];
F[j]=min(F[j],g[i][j]);
G[i]=min(G[i],f[i][j]);
}
printf("%lld",f[1][1]);
fclose(stdin);
fclose(stdout);
return 0;
}