[bzoj1783] [Usaco2010 Jan]Taking Turns

题意: 一排数,两个人轮流取数,保证取的位置递增,每个人要使自己取的数的和尽量大,求两个人都在最优策略下取的和各是多少。

注:双方都知道对方也是按照最优策略取的。。。

傻逼推了半天dp。。。。。。然后看kpm的代码里一个语句解决

 KPM大概思路:倒着取,设当前两人最大和分别为A和B(A为先取的人)。。如果B+W[i]>A就把A和B交换(先取的人可以按照更优的取法,后手无人权。。)..让A取W[i]

 接下来是蒟蒻的傻逼写法:

  f[0][i],f[1][i]分别表示第1个人和第2个人,在i~n中取了第i个数的最大总和。也是倒着取。。。

  f[0][i]=max{f[0][j1]}+W[i],(j1>i)所以我们维护一下j1就好了。但同时,因为有另一个人在取数,所以j1不可能随便取。。。

  因为取的位置递增,且对方也是按最优策略取的,所以i<j1<k1;(i<k1<=n且使得f[1][k1]在f[1][i+1.....n]中最大)。

  每次算f[0][i]的时候先更新一下k1和j1。。。求f[1][i]的时候同理。

  因为k1和j1是递减的,所以总的时间复杂度还是O(n)。

每次碰到有关博弈的题就抓瞎。。。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 const int maxn=700002;
 7 ll f[2][maxn],ans,ans1;
 8 int j1,k0,j0,k1,lastj1,lastk1;
 9 int val[maxn];
10 int i,j,k,n,m;
11 int ra;char rx;
12 inline int read(){
13     rx=getchar();ra=0;
14     while(rx<'0'||rx>'9')rx=getchar();
15     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra;
16 }
17 int main(){
18     n=read();
19     for(i=1;i<=n;i++)val[i]=read();j0=k0=n;j1=k1=n+1;lastj1=lastk1=n;
20     f[0][n]=f[1][n]=val[n];ans=n;
21     for(i=n-1;i;i--){
22         if(f[1][i+1]>=f[1][j0]){
23             j0=i+1;
24             for(j=lastj1;j>j0;j--)if(f[0][j]>=f[0][j1])j1=j;lastj1=j0+1;
25         }
26         f[0][i]=(ll)val[i]+f[0][j1];
27         if(f[0][i+1]>=f[0][k0]){
28             k0=i+1;
29             for(j=lastk1;j>k0;j--)if(f[1][j]>=f[1][k1])k1=j;lastk1=k0+1;
30         }
31         f[1][i]=(ll)val[i]+f[1][k1];
32         if(f[0][i]>=f[0][ans])ans=i;
33     }
34     for(i=ans+1;i<=n;i++)if(f[1][i]>ans1)ans1=f[1][i];
35     printf("%lld %lld\n",f[0][ans],ans1);
36 }
View Code

 

posted @ 2015-12-23 13:47  czllgzmzl  阅读(436)  评论(0编辑  收藏  举报