题目大意:一个无向图游戏中,R和B分别表示两个选手,两个人对图中的边轮流着色,R涂红色,B图蓝色,R优先,B的目标是,所有涂成蓝色的边和边相邻的点,构成一个生成子图,图中包含原图所有点,且连通,R的任务就是阻止B。问:B能否win,是,输出YES,否则,输出NO。
很明显是一道博弈题,我的做法是,首先进行缩点,对于相邻顶点u,v,如果u,v间的边数不小于2,则可以缩点,因为无论R对u,v间的边如何着色,都不可能阻止u,v的连通,缩完点以后,很明显,图中任意两个顶点间最多有一条边,此时,R开始涂色,他可以选取任意一条边涂色,然后递归到B涂色,如果所有的涂色方案中B都胜,则输出0, 否则输出1;对于B的涂色过程,B同样可以选择任意一条边涂色,他的涂色,就相当于缩点,对于(u,v)这条边,他现在为蓝色,他可以将所有其他与u相连的顶点和所有其他与v相邻的顶点连在一起,也就是说,如果另有一边(v,r),如果对边(v,r)的操作,等同于对(u,r)进行操作,对于(u,p)也一样,所以可以缩点,缩点完,如果只剩下一个点,自然无论如何R如何着色,B必胜,否则再递归到R着色。
能写出这种博弈代码,对我来说是多么的不容易啊,呵呵。。,下面是代码,就是有点长了。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h>
2 #include<string.h>
3 int n, m;
4
5 int SUO(int map[12][12], int mark[]);
6 int dfsB(int map[12][12], int mark[]);
7 int dfsR(int map[12][12], int mark[]);
8
9 //如果map[u][v] >= 2 缩点,并返回图中剩余的点数
10 int SUO(int map[12][12], int mark[]){
11 int flag, i, j, k;
12 flag = 1;
13 while(flag){
14 flag = 0;
15 for (i = 0; i < n; i++){
16 if (mark[i]) continue;
17 for (j = i + 1; j < n; j++){
18 if (!mark[j] && map[i][j] >= 2){
19 flag = 1;
20 break;
21 }
22 }
23 if (j < n) break;
24 }
25 if(flag){
26 mark[j] = 1;
27 for (k = 0; k < n; k++){
28 if (!mark[k] && k != i){
29 map[i][k] += map[j][k];
30 map[k][i] = map[i][k];
31 }
32 }
33 }
34 }
35 int cnt = 0;
36 for (i = 0; i < n; i++){
37 if (!mark[i]) cnt++;
38 }
39 return cnt;
40 }
41
42 // 轮到 player B 图色,返回B的博弈结果,1表示win
43 int dfsB(int map[12][12], int mark[]){
44 int i, j, t;
45 int map1[12][12];
46 int mark1[12];
47 for (i = 0; i < n; i++){
48 if (mark[i]) continue;
49 for (j = 0; j < n; j++){
50 if (!mark[j] && map[i][j]){
51 memcpy(map1, map, sizeof(map));
52 memcpy(mark1, mark, sizeof(mark));
53 map1[i][j] = 2;
54 if(SUO(map1, mark1) == 1) return 1;
55 t = dfsR(map1, mark1);
56 if (t == 0) return 1;
57 }
58 }
59 }
60 return 0;
61 }
62
63 // 轮到 player R 图色,返回R的博弈结果,1表示win
64 int dfsR(int map[12][12], int mark[]){
65 int map1[12][12];
66 int mark1[12];
67 int i, j, t, flag, cnt;
68 flag = 0;
69 for (i = 0; i < n; i++){
70 if (mark[i]) continue;
71 cnt = 0;
72 for (j = 0; j < n; j++){
73 if (!mark[j] && map[i][j]){
74 cnt++;
75 }
76 }
77 if (cnt <= 2){
78 flag = 1;
79 break;
80 }
81 }
82 if (flag) return 1;
83 for (i = 0; i < n; i++){
84 if (mark[i]) continue;
85 for (j = i + 1; j < n; j++){
86 if (!mark[j] && map[i][j]){
87 memcpy(map1, map, sizeof(map));
88 memcpy(mark1, mark, sizeof(mark1));
89 map1[i][j] = 0;
90 t = dfsB(map1, mark1);
91 if (t == 0) return 1;
92 }
93 }
94 }
95 return 0;
96 }
97
98 int main()
99 {
100 int a, b;
101 int map[12][12];
102 int mark[12];
103 while(scanf("%d%d", &n, &m) != EOF){
104 if (n == -1 && m == -1) break;
105 memset(map, 0, sizeof(map));
106 while(m--){
107 scanf("%d%d", &a, &b);
108 if (a != b){// 如果是自环,可以不考虑
109 map[a][b] += 1;
110 map[b][a] += 1;
111 }
112 }
113 memset(mark, 0, sizeof(mark));
114 if(SUO(map, mark) == 1) puts("YES");
115 else{
116 if (dfsR(map, mark)) puts("NO");
117 else puts("YES");
118 }
119 }
120 return 0;
121 }
122
加一组测试数据:
4 6
0 1
0 2
1 2
3 0
3 1
3 2
正解:YES