博弈问题

新学习了一下SG函数,很神奇的东西。

对于一类ICG游戏,用SG函数判定游戏的胜负态的方法。

首先说一下SG函数的定义以及性质:

首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3mex{2,3,5}=0mex{}=0

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Garundy函数g如下:g(x)=mex{ g(y) | yx的后继 }

所有的terminal position所对应的顶点,也就是没有出边的顶点qSG值为0,因为它的后继集合是空集。然后对于一个g(x)=0的顶点x,它的所有后继y都满足 g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0

求出每个游戏的SG值之后取异或就可以得出游戏是先手胜还是后手胜。

对于一般的ICG游戏,我们把各个游戏的SG只求出来后,就成为了一个nim游戏了!

今天的博弈题目UVALive 5059的代码如下:

 

ContractedBlock.gif ExpandedBlockStart.gif game theory
 
   
1 #include < cstdio >
2 #include < cmath >
3   using namespace std;
4   long long sg( long long a)
5 {
6 if (a & 1 )
7 return sg(a / 2 );
8 return a / 2 ;
9 }
10   int main()
11 {
12 int cases, n;
13 long long a;
14 scanf( " %d " , & cases);
15 while (cases -- )
16 {
17 scanf( " %d " , & n);
18 long long ans = 0 ;
19 for ( int i = 0 ;i < n;i ++ )
20 {
21 scanf( " %lld " , & a);
22 ans ^= sg(a);
23 }
24 printf( " %s\n " , ans == 0 ? " NO " : " YES " );
25 }
26 return 0 ;
27 }

HOJ 2838 Nim or not Nim?

吉牛为一年多校出的题目

总结SG函数,简单的推导一下(好吧,实际上可以猜的……)

long long sg(long long a)
{
    if(a % 4 == 0) return a - 1;
    if(a % 4 == 1) return a;
    if(a % 4 == 2) return a;
    if(a % 4 == 3) return a + 1;
}

正好是每四个一个循环,然后每次每次个就包含了从1 到 4 * k的所有的值。所以可以归纳出这个结论(纯YY,大牛无视吧)

ContractedBlock.gifHOJ 2838

树的删边的游戏

HOJ 2849 具体的证明是参见了09年的论文(在传说中的名师小班行上看的……)

得出的结论就是叶子节点的SG为0,其他节点的SG为其儿子节点的SG + 1的异或和。

代码如下:

ContractedBlock.gif ExpandedBlockStart.gif HOJ 2849
 
   
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4   using namespace std;
5   const int N = 50000 ;
6   struct edge
7 {
8 int v;
9 edge * nxt;
10 } * g[N], * pp, pool[ 2 * N];
11   bool chk[N];
12   int sg[N];
13   void addedge( int u, int v)
14 {
15 pp -> v = v;
16 pp -> nxt = g[u];
17 g[u] = pp ++ ;
18 }
19
20   void initialize()
21 {
22 pp = pool;
23 memset(g, 0 , sizeof (g));
24 memset(chk, 0 , sizeof (chk));
25 }
26   int dfs( int x)
27 {
28 chk[x] = true ;
29 sg[x] = 0 ;
30 for (edge * i = g[x];i != NULL;i = i -> nxt)
31 if ( ! chk[i -> v])
32 sg[x] ^= dfs(i -> v) + 1 ;
33 return sg[x];
34 }
35 int main()
36 {
37 int n;
38 while (scanf( " %d " , & n) == 1 )
39 {
40 int a, b;
41 initialize();
42 for ( int i = 1 ;i < n;i ++ )
43 {
44 scanf( " %d %d " , & a, & b);
45 a -- , b -- ;
46 addedge(a, b);
47 addedge(b, a);
48 }
49 printf( " %s\n " , dfs( 0 ) == 0 ? " Bob " : " Alice " );
50 }
51 return 0 ;
52 }

HOJ 2533 wywcgs教主还在工大的时候出的题

先贴代码

ContractedBlock.gif ExpandedBlockStart.gif HOJ 2849
 
   
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < vector >
5 using namespace std;
6 const int N = 100000 ;
7 int sg[N];
8 vector < int > ans;
9 int main()
10 {
11 int n, cases;
12 scanf( " %d " , & cases);
13 while (cases -- )
14 {
15 scanf( " %d " , & n);
16 ans.clear();
17 int isbig = 0 ;
18 int SG = 0 ;
19 for ( int i = 0 ;i < n;i ++ )
20 {
21 scanf( " %d " , & sg[i]);
22 SG ^= sg[i];
23 isbig += sg[i] > 1 ;
24 }
25 if (SG == 0 && ! isbig)
26 {
27 for ( int i = 0 ;i < n;i ++ )
28 ans.push_back( 1 );
29 }
30 else if (SG != 0 && isbig)
31 {
32 for ( int i = 0 ;i < n;i ++ )
33 {
34 isbig -= sg[i] > 1 ;
35 int temp = SG ^ sg[i];
36 if (temp <= sg[i] && isbig)
37 ans.push_back(sg[i] - temp);
38 else if ( ! isbig && temp == 1 )
39 ans.push_back(sg[i]);
40 else if ( ! isbig && temp == 0 )
41 ans.push_back(sg[i] - 1 );
42 else
43 ans.push_back( - 1 );
44 isbig += sg[i] > 1 ;
45 }
46 }
47 else
48 for ( int i = 0 ;i < n;i ++ )
49 ans.push_back( - 1 );
50 for ( int i = 0 ;i < n;i ++ )
51 {
52 if (i == 0 )
53 printf( " %d " , ans[i]);
54 else
55 printf( " %d " , ans[i]);
56 }
57 puts( "" );
58 }
59 return 0 ;
60 }

一类ANTI-SG类问题,具体的证明参考09年得论文

必胜态的要满足下面两个条件中的一个

1.整体游戏SG为0 且没有SG大于1的单个游戏

2.整体游戏SG不为0,且有大于1的单个游戏

这题要求出在每堆分别怎么取第一步可以取胜

如果游戏先手必输那么,每堆都是-1

如果除去某堆i,其他的堆还有大于1的,那么必须其它对的SG和(设为temp)为0才可以赢,此时必然有temp >= sg[i]。对应了条件2的反面。所以取走temp - sg[i]

如果除去某堆i,其他的堆没有大于1的,那么其它堆的异或和必然为0或者1, 如果为1的话,为了使其不能满足上述条件,必输全取走是SG 为1,(如果剩下一个就是SG为0 且没有大于1的(条件1),如果剩下多个就有SG不为0,有大于1的(条件2)),如果SG等于0的话,那么取走sg[i - 1]个就可以了满足条件2的反了!

PS.数据有点弱了

原来写的是

      if(temp <= sg[i] && isbig)
                    ans.push_back(sg[i] - temp);
                else if(!isbig && temp != sg[i])
                    ans.push_back(sg[i]);
                else
                    ans.push_back(-1);

都过了……例如 3 1 1 2这组就不对。

HOJ 2128

赤裸裸的根据SG函数的定义找SG值,递归记忆化写的有点慢了

ContractedBlock.gif ExpandedBlockStart.gif HOJ 2128
 
   
1 #include < iostream >
2 #include < cstring >
3 #include < vector >
4 #include < cstdio >
5 using namespace std;
6 const int S = 101 ;
7 const int N = 10001 ;
8 const int INF = 1000000 ;
9 int s[S], k;
10 int m, n ,h[N], sg[N];
11
12 int SG( int x)
13 {
14 if (sg[x] == - 1 )
15 {
16 vector < int > pre;
17 for ( int i = 0 ;i < k;i ++ )
18 {
19 if (x - s[i] >= 0 )
20 pre.push_back(SG(x - s[i]));
21 else
22 break ;
23 }
24 pre.push_back( - 1 );
25 pre.push_back(INF);
26 sort(pre.begin(), pre.end());
27 int n = pre.size();
28 for ( int i = 1 ;i < n;i ++ )
29 {
30 if (pre[i] - pre[i - 1 ] > 1 )
31 {
32 sg[x] = pre[i - 1 ] + 1 ;
33 break ;
34 }
35 }
36 }
37 return sg[x];
38 }
39 int main()
40 {
41 while (scanf( " %d " , & k) == 1 && k)
42 {
43 for ( int i = 0 ;i < k;i ++ )
44 scanf( " %d " , & s[i]);
45 sort(s, s + k);
46 scanf( " %d " , & m);
47 memset(sg, - 1 , sizeof (sg));
48 sg[ 0 ] = 0 ;
49 while (m -- )
50 {
51 int ans = 0 ;
52 scanf( " %d " , & n);
53 for ( int i = 0 ;i < n;i ++ )
54 {
55 scanf( " %d " , & h[i]);
56 ans ^= SG(h[i]);
57 }
58 printf( " %c " , ans == 0 ? ' L ' : ' W ' );
59 }
60 puts( "" );
61 }
62 return 0 ;
63 }

还有待更新……本周计算方法要考试,学习的进度慢了些呀……

转载于:https://www.cnblogs.com/ronaflx/archive/2011/04/10/2011946.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值