【NOIP2015模拟11.2晚】舳舻牌

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,|W­­­i­|≤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,jgk,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,jfk,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
#include
#include
#include
#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;
}

你可能感兴趣的:(博弈论,动态规划)