博弈SG函数

来源:互联网 发布:组策略映射网络驱动器 编辑:程序博客网 时间:2024/05/24 02:53

题意: 
一个棋盘有n行,每行20格子,都有一些棋子,两个人轮流进行这个操作:选择某一行一个棋子移动到该行右边第一个空的格子。不能进行的人输。问先手是否能赢。

分析: 

SG函数的应用,当时自己做的时候没做出来QAQ。终结点是这一行没有棋子可以走,即0,然后逆推出其他结点的SG函数。每一行的状态看成是一个结点,然后把状态二进制压缩,1表示有棋子,0表示空格。


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<list>
#include<map>
//#include<Windows.h>


using namespace std;


#define min(a,b) (a < b ? a:b)
#define max(a,b) (a > b ? a:b)


#define LL long long int


int sg[1 << 20],vis[21];


int setSG(int x)
{
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= 19; ++i)
{
if (x&(1 << i))
{
for (int j = i - 1; j >= 0; --j)//往右移动判断前态一定比越来的数小所以一定已经设置了状态
{
int t = x;
if (!(x&(1 << j)))
{
t ^= ((1 << i) ^ (1 << j));
vis[sg[t]] = 1;
break;
}
}
}
}
for (int i = 0; i <= 20; ++i)
{
if (vis[i] == 0)
{
return i;
}
}
}


int main()
{
int T, N, M ,x;




for (int i = 1; i < (1 << 20); ++i)
{
sg[i] = setSG(i);
}


scanf("%d", &T);
while (T--)
{
int ans = 0;
scanf("%d", &N);
for (int i = 0; i < N; ++i)
{
int t = 0;
scanf("%d", &M);
for (int j = 0; j < M; ++j)
{
scanf("%d", &x);
t ^= (1 << (20 - x));//因为SG函数是往左为正
}
ans ^= sg[t];
}
if (ans == 0)
{
printf("NO\n");
}
else
printf("YES\n");
}
//system("pause");
}



入门一:
首先来玩个游戏,引用杭电课件上的:
(1) 玩家:2人;
(2) 道具:23张扑克牌;
(3) 规则:
游戏双方轮流取牌;
每人每次仅限于取1张、2张或3张牌;
扑克牌取光,则游戏结束;
最后取牌的一方为胜者。
      想一下。。
      首先申明一点,博弈的讨论是在大家都玩的最好的情况下讨论的。(如果2个玩家智商有差别,那就没法讨论了~~~~开个玩笑哈。)
      介绍概念:P点 即必败点,某玩家位于此点,只要对方无失误,则必败;
                        N点 即必胜点,某玩家位于此点,只要自己无失误,则必胜。
定理:
     一、 所有终结点都是必败点P(上游戏中,轮到谁拿牌,还剩0张牌的时候,此人就输了,因为无牌可取);
    二、所有一步能走到必败点P的就是N点;
    三、通过一步操作只能到N点的就是P点;
    自己画下图看看。
    x :0  1  2  3  4  5  6  7  8  9  10。。。
pos:P   N N  N  P N  N  N  P N   N 。。。
    所以若玩家甲位于N点。只要每次把P点让给对方,则甲必胜;
   反之,若玩家甲位于P点,他每次只能走到N点,而只要乙每次把P点让给甲,甲必败;
    这里好好理解下;
   如果上面的理解的。请解决下面的题目:HDU 1846   2147(注意题目限制内存)(先2道练练手,做不出的话提示:找规律)
    接下来介绍Nim游戏(同样引用杭电上的,懒的打字)
   1.有两个玩家;
   2.  有三堆扑克牌(比如:可以分别是    5,7,9张);
  3. 游戏双方轮流操作;
  4. 玩家的每次操作是选择其中某一堆牌,然后从中取走任意张;
   5.最后一次取牌的一方为获胜方;
   想一会:
   还记得刚才说的P点和N点吗?P:必败点,N:必胜点
   先给出结论,这里要用到位运算,异或:^
    游戏的某个位置(x1,x2,x3) x1,x2,x3表示3堆的个数。当且仅当 x1^x2^x3=0时,此点才是必败点P;
    结论可以推广到一般情况,即有n堆,(x1,x2,x3,...xn) 当且仅当x1^x2^x3...^xn=0时,此点才是必败点P;
    如要看证明过程,链接在此    http://acm.hdu.edu.cn/forum/read.php?fid=9&tid=10617,看不懂的可以问 我(汗。。)
 练习:HDU 2188  2149   (做不出的话先看下面的,然后多思考)
   下面介绍sg函数(解决博弈问题的王道)
   sg 即Graph Game,把博弈游戏抽象成有向无环图
(1) 有向无环图
(2) 玩家1先移动,起点是x0
(3) 两个玩家轮流移动
(4) 对于顶点x, 玩家能够移动到的顶点集记为F(x).
(5) 不能移动的玩家会输掉游戏
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、 mex{2,3,5}=0、mex{}=0。
定义: 一个图的Sprague-Grundy函数(X,F)是定义在X上的非负函数g(x),并且满足:
       g(x) = mex{g(y) : y∈F(x)}
看到这里先好好理解一下sg值是怎么求的;
如果在取子游戏中每次只能取{1,2,3},那么各个数的SG值是多少?
x      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14. . .
g(x) 0 1 2 3 0 1 2 3 0 1  2   3   0   1   2. . .
看看这个和上面那个图的规律:
  P-点: 即令 g(x) = 0 的 x 点!
 N-点: 即令 g(x) > 0 的 x 点!
练习 HDU 1847  1849  1850 (做不出的话先看下面的,然后多思考)
最后看下组合博弈,就是把简单的游戏组合起来,比如3堆的可以看成3个一堆的游戏。
 定理:
假设游戏 Gi的SG函数是gi, i=1,…,n, 则
G = G1 + … + Gn 的 SG函数是
g(x1,…,xn) = g1(x1)⊕…⊕gn(xn).
其中那个符合就是异或^
看看是不是和Nim游戏的结论差不多?
如果想理解原理链接在此:http://www.cnitblog.com/weiweibbs/articles/42735.html
看完以上的,做完以下的练习。能理解完基本差不多可以算入门了:
HDU 1848 1517 1536(做不出就思考,思考,多看几遍)

0 0
原创粉丝点击