每天一道博弈论之“*游戏”(HNOI2007)

   题目链接:https://www.luogu.org/problemnew/show/P3185

  题解:

  首先这道题第一眼望过去好像不是很简单qwq,那么我们先尝试看能不能发现一些特殊性质。

  我们先从后手相消的原则去看,则可以发现若一个瓶子中有偶数个的话,那么这个瓶子是没有意义的。因为后手可以完全复制先手的操作,则该瓶子内减去偶数个巧克力豆还是偶数个,新增的两个瓶子内也都是增加了偶数个。

  于是我们就只考虑奇数个的瓶子。而且明显只要当成1就可以了,因为取掉1之后就是偶数,就可以当做不存在了。那么原序列就变成了一个01序列。、

  假如我们了解过multi-nim模型的话我们就可以发现其实剩下的操作与multi-nim是很像的。

 什么是multi-nim呢?

  n堆石子,两种操作,其一为在一堆石子中拿走任意个(>0),其二为把一堆石子分成两堆,这两堆石子总数等于原石子数,且都是正整数。不能操作时输。

  可以发现每堆石子是独立的。一堆石子数为x的sg值为mex{sg[y],sg[i]^sg[j]}(0<=y<x,i+j==x,i>0,j>0)

  我们回到原题。

  我们可以观察到每个1所对应的游戏状态是独立的,那么我们算出每个1的sg值再用sg定理即可解决。我们可以把01序列中1的位置对应到multi-nim中的值。比如样例中序列(1,0,1,5000),转换成01序列为(1,0,1)(最后一位不能移动,不需考虑),那么其对应的游戏状态为(1,3),只不过这时与multi-nim不同的一点就是此时一个数分成的两个数之和不一定要等于原数,而且可以为零。

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;

inline int read() {
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-'0' ;
    return k*f ;
}

int n ; int sg[23], a[23] ;

int dfs(int x) {
    if(sg[x]) return sg[x] ;
    bool v[100] = {0} ;
    for(int i=x+1;i<=n;i++)
      for(int j=i;j<=n;j++)
        v[dfs(i)^dfs(j)] = 1 ;
    for(int i=0; ;i++)
      if(!v[i]) {
          sg[x] = i ; return i ;
      }
}

int main() {
    int t = read() ;
    while(t--) {
        n = read() ; memset(sg,0,sizeof(sg)) ;
        for(int i=1;i<=n;i++) a[i] = read() ;
        int res = 0 ;
        for(int i=1;i<n;i++) if(a[i]&1) res ^= dfs(i) ;
        int tot = 0 ;
        for(int i=1;i<n;i++) {
//            if(!(a[i]&1)) continue ;  // 虽然我们在转换成multi-nim游戏后不用考虑那些偶数点
                                      // 但从方案的角度考虑先移偶数也可能会到达必败态 
            for(int j=i+1;j<=n;j++) 
              for(int k=j;k<=n;k++) {
                  if(!(dfs(j)^dfs(k)^dfs(i)^res)) {  // 当该位原本为奇数时,dfs(i)^res即删去原位的i
                                                   // 再亦或上dfs(j)^dfs(k)即在i为与j位加上1
                                                   // 当该位原本为偶数时,res表示原游戏sg值
                                                   // 移动导致i,j,k三个位置都发生奇偶变化,即新增三个游戏,所以要亦或上三个游戏的sg值 
                      tot ++ ;
                      if(tot == 1) printf("%d %d %d
",i-1,j-1,k-1) ;
                  }
              }
        }
        if(!tot) printf("-1 -1 -1
") ;
        printf("%d
",tot) ;
    }
    return 0 ;
}
View Code

  错误原因:一开始没有考虑到虽然对于胜负来说偶数无所谓,但对于方案来说还是可以移动偶数的。所以只要把那句跳过偶数的注释掉就ok了。

 (博弈论主要是个人的思考与理解,很难讲明白,本博客只希望其中的某几句话能激发大家的灵感即可)

 ——end ;

原文地址:https://www.cnblogs.com/zub23333/p/8548182.html