hdu3267 Graph Game 缩点 + 博弈

Graph Game

题目大意:一个无向图游戏中,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着色。

 

能写出这种博弈代码,对我来说是多么的不容易啊,呵呵。。,下面是代码,就是有点长了。。。

 

代码
   
     
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

你可能感兴趣的:(Graph)