[luoguP3355] 骑士共存问题(二分图最大独立集)
模型
二分图最大独立集,转化为二分图最大匹配,从而用最大流解决。
实现
首先把棋盘黑白染色,使相邻格子颜色不同。
把所有可用的黑色格子看做二分图X集合中顶点,可用的白色格子看做Y集合顶点。
建立附加源S汇T,从S向X集合中每个顶点连接一条容量为1的有向边,从Y集合中每个顶点向T连接一条容量为1的有向边。
从每个可用的黑色格子向骑士一步能攻击到的可用的白色格子连接一条容量为无穷大的有向边。
求出网络最大流,要求的结果就是可用格子的数量减去最大流量。
分析
用网络流的方法解决棋盘上的问题,一般都要对棋盘黑白染色,使之成为一个二分图。放尽可能多的不能互相攻击的骑士,就是一个二分图最大独立集问题。有关二分图最大独立集问题,更多讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛。
该题规模比较大,需要用效率较高的网络最大流算法解决。(使用Dinic+当前弧优化)
——代码
1 #include <queue> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #define INF 1e9 6 #define N 1000001 7 #define min(x, y) ((x) < (y) ? (x) : (y)) 8 9 int n, m, cnt, tot, sum, s, t; 10 int map[201][201], head[N], to[N], val[N], next[N], dis[N], cur[N]; 11 int dx[8] = {-2, -1, 1, 2, 2, 1, -1, -2}, 12 dy[8] = {1, 2, 2, 1, -1, -2, -2, -1}; 13 14 inline int read() 15 { 16 int x = 0, f = 1; 17 char ch = getchar(); 18 for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; 19 for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; 20 return x * f; 21 } 22 23 inline void add2(int x, int y, int z) 24 { 25 to[cnt] = y; 26 val[cnt] = z; 27 next[cnt] = head[x]; 28 head[x] = cnt++; 29 } 30 31 inline void add(int x, int y, int z) 32 { 33 add2(x, y, z); 34 add2(y, x, 0); 35 } 36 37 inline bool bfs() 38 { 39 int i, u, v; 40 std::queue <int> q; 41 memset(dis, -1, sizeof(dis)); 42 q.push(s); 43 dis[s] = 0; 44 while(!q.empty()) 45 { 46 u = q.front(), q.pop(); 47 for(i = head[u]; i ^ -1; i = next[i]) 48 { 49 v = to[i]; 50 if(val[i] && dis[v] == -1) 51 { 52 dis[v] = dis[u] + 1; 53 if(v == t) return 1; 54 q.push(v); 55 } 56 } 57 } 58 return 0; 59 } 60 61 inline int dfs(int u, int maxflow) 62 { 63 if(u == t) return maxflow; 64 int v, d, ret = 0; 65 for(int &i = cur[u]; i ^ -1; i = next[i]) 66 { 67 v = to[i]; 68 if(val[i] && dis[v] == dis[u] + 1) 69 { 70 d = dfs(v, min(val[i], maxflow - ret)); 71 ret += d; 72 val[i] -= d; 73 val[i ^ 1] += d; 74 if(ret == maxflow) return ret; 75 } 76 } 77 return ret; 78 } 79 80 int main() 81 { 82 int i, j, k, x, y; 83 n = read(); 84 m = read(); 85 s = 0, t = n * n + 1; 86 memset(head, -1, sizeof(head)); 87 for(i = 1; i <= m; i++) 88 { 89 x = read(); 90 y = read(); 91 map[x][y] = -1; 92 } 93 for(i = 1; i <= n; i++) 94 for(j = 1; j <= n; j++) 95 if(!map[i][j]) 96 { 97 map[i][j] = ++tot; 98 if((i + j) & 1) add(s, tot, 1); 99 else add(tot, t, 1); 100 } 101 for(i = 1; i <= n; i++) 102 for(j = 1; j <= n; j++) 103 if(map[i][j] ^ -1 && (i + j) & 1) 104 for(k = 0; k < 8; k++) 105 { 106 x = i + dx[k]; 107 y = j + dy[k]; 108 if(x >= 1 && x <= n && y >= 1 && y <= n && map[x][y] ^ -1) add(map[i][j], map[x][y], INF); 109 } 110 while(bfs()) 111 { 112 for(i = s; i <= t; i++) cur[i] = head[i]; 113 sum += dfs(s, INF); 114 } 115 printf("%d\n", tot - sum); 116 return 0; 117 }