Description
A 公司正在举办一个智力双人游戏比赛----取石子游戏,游戏的获胜者将会获得 A 公司提供的丰厚奖金,因此吸引了来自全国各地的许多聪明的选手前来参加比赛。
与经典的取石子游戏相比,A公司举办的这次比赛的取石子游戏规则复杂了很多:
? 总共有N堆石子依次排成一行,第i堆石子有 ai个石子。
? 开始若干堆石子已被 A公司故意拿走。
? 然后两个玩家轮流来取石子,每次每个玩家可以取走一堆中的所有石子,但有一个限制条件:一个玩家若要取走一堆石子,则与这堆石子相邻的某堆石子已被取走(之前被某个玩家取走或开始被A公司故意拿走)。注意:第 1堆石子只与第 2堆石子相邻,第N堆石子只与第N-1堆石子相邻,其余的第 i堆石子与第i-1堆和第 i+1 堆石子相邻。
? 所有石子都被取走时,游戏结束。谁最后取得的总石子数最多,谁就获得了这场游戏的胜利。
作为这次比赛的参赛者之一,绝顶聪明的你,想知道对于任何一场比赛,如果先手者和后手者都使用最优的策略,最后先手者和后手者分别能够取得的总石子数分别是多少。
Input
第一行是一个正整数N,表示有多少堆石子。输入文件第二行是用空格隔开的N个非负整数a1, a2, …, aN,其中ai表示第i堆石子有多少个石子,
ai = 0表示第i堆石子开始被A公司故意拿走。输入的数据保证0≤ai≤100,000,000,并且至少有一个i使得ai = 0。30%的数据满足2≤N≤100,100%的数据满足2≤N≤1,000,000。
Output
仅包含一行,为两个整数,分别表示都使用最优策略时,最后先手者和后手者各自能够取得的总石子数,并且两个整数间用一个空格隔开。
Sample Input
1 2 0 3 7 4 0 9
Sample Output
正解:博弈论+链表。
这道题的难点在于分析博弈者的心理。
双方的目标就是使自己-别人的石子数差最大化。
可以看出,石子堆分为两个栈和若干个双端队列。
如果所有元素在每一个容器中都是递减的,那么先手和后手显然可以每次都取最大元素来保证自己利益的最大化。
但是现在不是这样,不过我们可以把这个数列化简一下。
如果序列最左端是$A,B...$或最右端是$...B,A$,且$B\leq A$。
那么双方在有其它方案时都不会愿意先取走$B$,故这种情况可以留到博弈的最后。
由于石子数是确定的,可以直接推出最后谁取到了$A$,算出相应差值。
由于可以留到游戏的最后,此时删除这两堆并不影响两人之前的决策。
如果有一段 $...A,B,C...$且满足$A\leq B,C\leq B$。
那么我们直接把 ABC 替换成一个 A+C-B 即可。
我们可以这样想:选$A,B,C$的时候是因为没有更好的决策而被迫选的。事实上当全场没有大于$A+C-B$的石子堆可以直接取时,才会考虑取$A,C$中的一个。那么不管第一次取$A,B,C$中的元素是从哪边,后手一定也没有别的更好的选择,既然先手选$A,C$都已是被迫了,所以后手选$B$一定不会是差的。留下来的一个也一定是当前不差的选择。故先手一定取走$A+C$,后手取走$B$。从对分数差的贡献来看,我们可以直接把$A,B,C$代替成$A+C-B$。
我们用链表实现化简,就能把石子变成递减的情况,然后直接排序取石子就行了。
1 #include <bits/stdc++.h> 2 #define il inline 3 #define RG register 4 #define ll long long 5 #define N (1000005) 6 7 using namespace std; 8 9 int b[N],l[N],r[N],n,h,t,ok,cnt,tmp; 10 ll a[N],c[N],sum,dif; 11 12 il int gi(){ 13 RG int x=0,q=1; RG char ch=getchar(); 14 while ((ch<‘0‘ || ch>‘9‘) && ch!=‘-‘) ch=getchar(); 15 if (ch==‘-‘) q=-1,ch=getchar(); 16 while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-48,ch=getchar(); 17 return q*x; 18 } 19 20 il void del(RG int x){ 21 RG int L=l[x],R=r[x]; 22 if (L) r[L]=R,l[x]=0; 23 if (R<=n) l[R]=L,r[x]=n+1; 24 return; 25 } 26 27 int main(){ 28 #ifndef ONLINE_JUDGE 29 freopen("stone.in","r",stdin); 30 freopen("stone.out","w",stdout); 31 #endif 32 n=gi(),h=1,t=n,ok=1; 33 for (RG int i=1;i<=n;++i) 34 a[i]=gi(),sum+=a[i],b[i]=!a[i],cnt^=!b[i],l[i]=i-1,r[i]=i+1; 35 while (ok && h<t){ 36 ok=0; 37 while (h<t && !b[h] && !b[r[h]] && a[h]>=a[r[h]]){ 38 cnt?dif+=a[h]-a[r[h]]:dif+=a[r[h]]-a[h]; 39 tmp=r[r[h]],del(r[h]),del(h),h=tmp,ok=1; 40 } 41 while (h<t && !b[t] && !b[l[t]] && a[t]>=a[l[t]]){ 42 cnt?dif+=a[t]-a[l[t]]:dif+=a[l[t]]-a[t]; 43 tmp=l[l[t]],del(l[t]),del(t),t=tmp,ok=1; 44 } 45 for (RG int i=h;i<=t;i=r[i]){ 46 if (b[i] || !l[i] || r[i]==n+1 || b[l[i]] || b[r[i]]) continue; 47 if (a[i]<a[l[i]] || a[i]<a[r[i]]) continue; 48 if (h==l[i]) h=i; if (t==r[i]) t=i; 49 a[i]=a[l[i]]+a[r[i]]-a[i],del(l[i]),del(r[i]),ok=1; 50 } 51 } 52 cnt=0; for (RG int i=h;i<=t;i=r[i]) if (!b[i]) c[++cnt]=a[i]; 53 sort(c+1,c+cnt+1),reverse(c+1,c+cnt+1); 54 for (RG int i=1;i<=cnt;++i) i&1?dif+=c[i]:dif-=c[i]; 55 printf("%lld %lld\n",(sum+dif)>>1,(sum-dif)>>1); return 0; 56 }