骑士共存问题
Posted mrclr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了骑士共存问题相关的知识,希望对你有一定的参考价值。
这是一道比较经典的最小割模型,对只会最大流却对最小割一窍不通的我来说在适合不过了。
首先,题目中的图片非常良心,细心观察他能得到一个很重要的规律:黄色格子上的骑士只能攻击红色格子上的骑士,反之同理。
因此,我们可以把棋盘进行黑白染色,然后白点放在图的左侧,黑点在图的右侧,有点像二分图的感觉。
接下来切入正题了:做最小割的题,源点和汇点往往没有什么实际意义,重点是割掉每一条边的意义。每一条边代表一个冲突,割掉它就是付出的代价。由此可见,最小割一般是反着想,比如源点和汇点不连通了,代表所有冲突解决了。
具体连边是这样的:
1.源点像白点连一条边权为1的边,代表如果割掉这条边,就是拿走了这个棋子,代价就是1.
2.同理黑点向汇点连一条边权为1的边。
3.考虑一个白点能攻击到的所有黑点,就像这些黑点连一条INF的边。为什么是INF呢?因为这条边的意义代表两个骑士会互相攻击,不能共存,割掉它代表两个骑士能共存了,这显然是不可能的,一次付出的代价是INF。
4.图中还有障碍点。表示骑士一定不能放到这个点,因此一定会付出1的代价,而不用考虑他是否会割掉,因此不用把他连边建图。
所以最终的答案就是n2 - minCut - m.
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(‘ ‘) 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 4e4 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ‘ ‘; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - ‘0‘; ch = getchar();} 27 if(last == ‘-‘) ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar(‘-‘); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + ‘0‘); 35 } 36 37 const int dx[10] = {-1, -2, -2, -1, 1, 2, 2, 1}, dy[10] = {-2, -1, 1, 2, 2, 1, -1, -2}; 38 int n, m, t; 39 bool vis[maxn]; 40 41 struct Edge 42 { 43 int from, to, cap, flow; 44 }; 45 vector<Edge> edges; 46 vector<int> G[maxn]; 47 void addEdge(int from, int to, int w) 48 { 49 edges.push_back((Edge){from, to, w, 0}); 50 edges.push_back((Edge){to, from, 0, 0}); 51 int sz = edges.size(); 52 G[from].push_back(sz - 2); 53 G[to].push_back(sz - 1); 54 } 55 56 int getnum(int x, int y) 57 { 58 return (x - 1) * n + y; 59 } 60 61 void solve(int x, int y) 62 { 63 int u = getnum(x, y); 64 for(int i = 0; i < 8; ++i) 65 { 66 int newx = x + dx[i], newy = y + dy[i]; 67 if(newx > 0 && newx <= n && newy > 0 && newy <= n) 68 addEdge(u, getnum(newx, newy), INF); 69 } 70 } 71 72 int dis[maxn]; 73 bool bfs() 74 { 75 Mem(dis, 0); dis[0] = 1; 76 queue<int> q; q.push(0); 77 while(!q.empty()) 78 { 79 int now = q.front(); q.pop(); 80 for(int i = 0; i < (int)G[now].size(); ++i) 81 { 82 Edge& e = edges[G[now][i]]; 83 if(!dis[e.to] && e.cap > e.flow) 84 { 85 dis[e.to] = dis[now] + 1; 86 q.push(e.to); 87 } 88 } 89 } 90 return dis[t]; 91 } 92 int cur[maxn]; 93 int dfs(int now, int res) 94 { 95 if(now == t || res == 0) return res; 96 int flow = 0, f; 97 for(int& i = cur[now]; i < (int)G[now].size(); ++i) 98 { 99 Edge& e = edges[G[now][i]]; 100 if(dis[e.to] == dis[now] + 1 && (f = dfs(e.to, min(res, e.cap - e.flow))) > 0) 101 { 102 e.flow += f; 103 edges[G[now][i] ^ 1].flow -= f; 104 flow += f; res -= f; 105 if(res == 0) break; 106 } 107 } 108 return flow; 109 } 110 111 int minCut() 112 { 113 int flow = 0; 114 while(bfs()) 115 { 116 Mem(cur, 0); 117 flow += dfs(0, INF); 118 } 119 return flow; 120 } 121 122 int main() 123 { 124 n = read(); m = read(); 125 t = n * n + 1; 126 for(int i = 1; i <= m; ++i) 127 { 128 int x = read(), y = read(); 129 vis[getnum(x, y)] = 1; 130 } 131 for(int i = 1; i <= n; ++i) 132 for(int j = 1; j <= n; ++j) if(!vis[getnum(i, j)]) 133 { 134 if((i + j) & 1) addEdge(getnum(i, j), t, 1); 135 else addEdge(0, getnum(i, j), 1), solve(i, j); 136 } 137 write(n * n - m - minCut()); enter; 138 return 0; 139 }
以上是关于骑士共存问题的主要内容,如果未能解决你的问题,请参考以下文章