题意:给定网格图,有障碍。
你要用若干条蛇把所有空地覆盖起来。
满足:每条蛇要么成环,要么头尾都在边界。
如果一条蛇同时满足,那么算成环。
要使头尾都在边界的蛇最少。
解:
一开始想用一个流量代表一条蛇,顺着这个思路想了很久都没想出来。最后终于看了题解。
如果把蛇看做边,那么每个空地都要和旁边两个空地相连。或者在边界。
然后我们又可以黑白染色一波。
这样的话,我们指定流量从白到黑,那么每个白色就应该获得两个流量以便流出去,而每个黑的要接受两个流量。
所以从s向白连流量限制为[2, 2]的边,黑色向t同样。
白向相邻黑连流量限制[0, 1]的边。
边界怎么处理?白向t连[0, 1],费用为1的边。s向黑同样。
然后求有源汇有上下界最小费用可行流即可。
输出答案时要/2,因为头尾各算了一次。
1 /************************************************************** 2 Problem: 4213 3 Language: C++ 4 Result: Accepted 5 Time:144 ms 6 Memory:32128 kb 7 ****************************************************************/ 8 9 #include <cstdio> 10 #include <algorithm> 11 #include <queue> 12 #include <cstring> 13 #include <string> 14 15 const int N = 1000, M = 1000010, INF = 0x3f3f3f3f; 16 const int dx[] = {0, 1, 0, -1}; 17 const int dy[] = {1, 0, -1, 0}; 18 19 struct Edge { 20 int nex, v, c, len; 21 }edge[M << 1]; int top = 1; 22 23 int e[N], d[N], vis[N], pre[N], flow[N], G[20][20], m, n, lm, ot[N]; 24 std::queue<int> Q; 25 char str[20]; 26 27 inline void add(int x, int y, int z, int w) { 28 top++; 29 edge[top].v = y; 30 edge[top].c = z; 31 edge[top].len = w; 32 edge[top].nex = e[x]; 33 e[x] = top; 34 35 top++; 36 edge[top].v = x; 37 edge[top].c = 0; 38 edge[top].len = -w; 39 edge[top].nex = e[y]; 40 e[y] = top; 41 return; 42 } 43 44 inline bool SPFA(int s, int t) { 45 memset(d, 0x3f, sizeof(d)); 46 d[s] = 0; 47 flow[s] = INF; 48 vis[s] = 1; 49 Q.push(s); 50 while(!Q.empty()) { 51 int x = Q.front(); 52 Q.pop(); 53 vis[x] = 0; 54 for(int i = e[x]; i; i = edge[i].nex) { 55 int y = edge[i].v; 56 if(edge[i].c && d[y] > d[x] + edge[i].len) { 57 d[y] = d[x] + edge[i].len; 58 pre[y] = i; 59 flow[y] = std::min(flow[x], edge[i].c); 60 if(!vis[y]) { 61 vis[y] = 1; 62 Q.push(y); 63 } 64 } 65 } 66 } 67 return d[t] < INF; 68 } 69 70 inline void update(int s, int t) { 71 int temp = flow[t]; 72 while(t != s) { 73 int i = pre[t]; 74 edge[i].c -= temp; 75 edge[i ^ 1].c += temp; 76 t = edge[i ^ 1].v; 77 } 78 return; 79 } 80 81 inline int solve(int s, int t, int &cost) { 82 int ans = 0; 83 cost = 0; 84 while(SPFA(s, t)) { 85 ans += flow[t]; 86 cost += flow[t] * d[t]; 87 update(s, t); 88 } 89 return ans; 90 } 91 92 inline int id(int x, int y, int f = 0) { 93 int ans = (x - 1) * m + y; 94 return ans; 95 } 96 97 inline void judge(int x) { 98 for(int i = e[x]; i; i = edge[i].nex) { 99 if(edge[i].c) { 100 printf("ss -> %d c = %d ", edge[i].v, edge[i].c); 101 } 102 } 103 return; 104 } 105 106 int main() { 107 108 int i = 0, sum = 0; 109 while(scanf("%s", str + 1) != EOF) { 110 i++; 111 m = strlen(str + 1); 112 for(int j = 1; j <= m; j++) { 113 G[i][j] = (str[j] == '#'); 114 } 115 } 116 n = i; 117 lm = n * m; 118 int s = lm + 1, t = lm + 2, ss = lm + 3, tt = lm + 4; 119 for(i = 1; i <= n; i++) { 120 for(int j = 1; j <= m; j++) { 121 if(G[i][j]) { 122 continue; 123 } 124 if((i + j) & 1) { 125 // s - 2 > (i, j) 126 add(ss, id(i, j), 2, 0); 127 sum += 2; 128 ot[s] += 2; 129 for(int k = 0; k < 4; k++) { 130 int x = i + dx[k]; 131 int y = j + dy[k]; 132 if(x && y && x <= n && y <= m && !G[x][y]) { 133 add(id(i, j), id(x, y), 1, 0); 134 } 135 } 136 if(i == 1 || j == 1 || i == n || j == m) { 137 add(id(i, j), t, 1, 1); 138 } 139 } 140 else { 141 // (i, j) - 2 > t 142 add(id(i, j), tt, 2, 0); 143 ot[t] -= 2; 144 if(i == 1 || j == 1 || i == n || j == m) { 145 add(s, id(i, j), 1, 1); 146 } 147 } 148 149 } 150 } 151 add(s, tt, ot[s], 0); 152 add(ss, t, -ot[t], 0); 153 add(t, s, INF, 0); 154 sum += -ot[t]; 155 int ans; 156 157 if(solve(ss, tt, ans) != sum) { 158 puts("-1"); 159 return 0; 160 } 161 printf("%d", ans / 2); 162 return 0; 163 }
hzwer巨佬写了个随机排列起点然后贪心的乱搞,居然有70pts+ %%%