POJ 3710 Christmas Game [博弈]

题意:略。

思路:这是个删边的博弈游戏。

关于删边游戏的预备知识:http://blog.csdn.net/acm_cxlove/article/details/7854532

学习完预备知识后,这一题就不难了。

首先,用tarjan算法找到每棵树中的双连通分量(即树枝上的多边形),根据Fusion Principle,如果多边形有奇数条边,可以简化为1条边,如果有偶数条边,则可以简化为1个点。代码中使用了vis数组,对于前者,使环内所有的点(包括悬挂点)的vis值为1,后面计算sg值时便不会再进行遍历这些点;对于后者,除了悬挂点和多边形内与悬挂点相邻的一点(相邻点中只取一个)外,多边形内其他点vis为1,这样就相当于将环化为了1条边。另外,题目当中会有重边,根据题意,当出现这种情况时,一定也是出现在树枝的末端。这里直接当作多边形来处理,方法是相同的,如果有偶数条边,化为1个点,奇数条边化为1条边。

然后由Colon Principle,可以计算出整棵树的sg值,之后对于所有的树,就是个Nim游戏了。

更多细节看代码

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stack>
  4 #include<algorithm>
  5 #define maxn 105
  6 #define maxp 2000
  7 using namespace std;
  8 int map[maxn][maxn];
  9 bool vis[maxn];
 10 struct node
 11 {
 12     int v, next;
 13 }edge[maxp];
 14 int num_edge, head[maxn];
 15 void init()
 16 {
 17     num_edge = 0;
 18     memset(head, -1, sizeof(head));
 19 }
 20 void addedge(int a,int b)
 21 {
 22     edge[num_edge].v = b;
 23     edge[num_edge].next = head[a];
 24     head[a] = num_edge++;
 25 }
 26 struct scc
 27 {
 28     int dfn[maxn], low[maxn];
 29     int cnt, scnt;
 30     bool instack[maxn];
 31     stack<int> s;
 32     void init()
 33     {
 34         cnt = scnt = 0;
 35         memset(dfn, -1, sizeof(dfn));
 36         memset(instack, 0, sizeof(instack));
 37     }
 38     void tarjan(int u,int father)
 39     {
 40         dfn[u] = low[u] = ++cnt;
 41         s.push(u);
 42         instack[u] = 1;
 43         for (int i = head[u]; i != -1; i = edge[i].next)
 44         {
 45             int v = edge[i].v;
 46             if (v == father)
 47             {
 48                 if (map[u][v] > 1 && map[u][v] % 2 == 0)
 49                     vis[u] = 1;
 50                 continue;
 51             }
 52             if (dfn[v] == -1)
 53             {
 54                 tarjan(v, u);
 55                 low[u] = min(low[u], low[v]);
 56             }
 57             else if (instack[v]) low[u] = min(low[u], dfn[v]);
 58         }
 59         if (dfn[u] == low[u])
 60         {
 61             scnt = 0;
 62             int last;
 63             for (;;)
 64             {
 65                 int temv = s.top();
 66                 s.pop();
 67                 scnt++;
 68                 instack[temv] = 0;
 69                 if ((temv == u) || s.empty())
 70                     break;
 71                 vis[temv] = 1;
 72                 last = temv;
 73             }
 74             if (scnt & 1)
 75                 vis[last] = 0;
 76         }
 77         return;
 78     }
 79     void solve(int n)
 80     {
 81         init();
 82         for (int i = 1; i <= n; i++) if (dfn[i] == -1)
 83             tarjan(i, -1);
 84     }
 85 };
 86 int getsg(int u,int father)
 87 {
 88     int osum = 0;
 89     for (int i = head[u]; i != -1; i = edge[i].next)
 90     {
 91         int v = edge[i].v;
 92         if (!vis[v] && v != father)
 93             osum ^= (1 + getsg(v, u));
 94     }
 95     return osum;
 96 }
 97 int main()
 98 {
 99     int k, n, m;
100     scc g;
101     while (~scanf("%d", &n))
102     {
103         int osum = 0;
104         for (int i = 1; i <= n; i++)
105         {
106             scanf("%d%d", &m, &k);
107             int a, b;
108             init();
109             memset(map, 0, sizeof(map));
110             memset(vis, 0, sizeof(vis));
111             while (k--)
112             {
113                 scanf("%d%d", &a, &b);
114                 addedge(a, b);
115                 map[a][b]++;
116                 addedge(b, a);
117                 map[b][a]++;
118             }
119             g.solve(m);
120             osum ^= getsg(1, -1);
121         }
122         if (osum) printf("Sally\n");
123         else printf("Harry\n");
124     }
125     return 0;
126 }

 

posted @ 2013-09-05 21:01  fenshen371  阅读(382)  评论(0编辑  收藏  举报