[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 }