网络流真的是一种神奇的算法。在一张图上面求感觉高度自动化的方案一般而言好像都是网络流的主阵地。讲真一开始看到这道题也有点懵,题面很长,感觉很难的样子。不过,仔细阅读了题意之后明白了:我们所要做的就是要用最小的代价,使得最后的图中不能出现给定的四种图案。
实际上之前做过一道非常毒瘤的网络流题目【无限之环】。当时就思考了一下:为什么出题人规定不能旋转直线管子?原因就是因为这样的图建不出来,它与不具有其他的管子的特点。那么可以断定:给出的这四个图案必然也是非常特殊的图形,否则不会只有这四个/不会是这四个。于是观察这四个图案,不难发现它们的特征:以一对中间夹了特殊边的方块为中心,分别有另一方块随意连接在上方。
我们从这个中心开始入手:由于要求最小值,所以-->最小割 / 费用流。但一个直观的感觉,它是在很多的方案当中做出选择,与最小割是比较贴合的。(每一种图案牵扯到四个方块,拿掉任何一个就可以破坏这个图形)如果每一种方案彼此平行,只需建出图暴力跑即可。可是会有交叉:当我们删去一个方块时,可能同时破坏了两个方案。
观察图案,由于上下,左右交错,一个中心只可能出现在一个方案中,但其两侧的方块却可能出现在不同的方案当中。于是我们对于这样两侧的方块进行黑白染色,保证每一种方案当中所牵涉到的另两个方块分别属于不同的颜色(由于题目的特殊性质是可以做到的)。
那么建边的方式也十分的自然了:
源点-->所有白色的点,边权为代价;所有白色的点-->中心连边,边权为INF;所有的中心 = 两条边 = 边权为各个方块的代价。所有的中心 --> 黑点,黑点 --> 汇点,边权为代价。此时的最小割所代表的意义即为我们做出的最小代价方案选择。
做出这题还是比较开心的,代码跑得也很快。不过细节比较多,要注意一下……
// luogu-judger-enable-o2 #include <bits/stdc++.h> using namespace std; #define maxn 300000 #define maxm 800000 #define ll long long #define INF 9999999 int C, R, n, S, T, tot; int Color[maxn], cnt; int cnp = 2, head[maxn], cur[maxn]; int lev[maxn]; map <int, int> Map; int dx[10] = {1, 1, 1, 1, 0, 0, -1, -1, -1, -1}; int dy[10] = {-1, 0, 1, 2, -1, 2, -1, 0, 1, 2}; struct edge { int to, last, f; }E[maxm]; struct node { int x, y, w; }P[maxn]; bool cmp(node a, node b) { if(a.x != b.x) return a.x < b.x; return a.y < b.y; } int read() { int x = 0, k = 1; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) k = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar(); return x * k; } void add(int u, int v, int f) { E[cnp].to = v, E[cnp].f = f, E[cnp].last = head[u], head[u] = cnp ++; E[cnp].to = u, E[cnp].f = 0, E[cnp].last = head[v], head[v] = cnp ++; } bool Bfs() { queue <int> q; memset(lev, 0, sizeof(lev)); q.push(S); lev[S] = 1; while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(!lev[v] && E[i].f) { lev[v] = lev[u] + 1; q.push(v); } } if(lev[T]) return 1; } return 0; } int Dfs(int u, int nf) { if(u == T) return nf; int lf = 0; for(int i = cur[u]; i; i = E[i].last) { int v = E[i].to; if(lev[v] == lev[u] + 1 && E[i].f) { int af = Dfs(v, min(E[i].f, nf)); nf -= af, lf += af; E[i].f -= af, E[i ^ 1].f += af; if(!nf) return lf; cur[u] = i; } } if(!lf) lev[u] = -1; return lf; } int Dinic() { int ans = 0; while(Bfs()) { memcpy(cur, head, sizeof(head)); ans += Dfs(S, INF); } return ans; } int id(int x, int y) { ll k = 1ll * R * (x - 1) + 1ll * y; if(Map[k]) return Map[k]; else return Map[k] = ++ tot; } int Check(int x, int y) { ll k = 1ll * R * (x - 1) + 1ll * y; if(Map[k]) return Map[k]; else return 0; } void Get_Graph(int p) { int a = ++ tot, b = ++ tot, c = ++ tot; add(a, b, P[p].w); add(b, c, P[p + 1].w); for(int i = 0; i < 10; i ++) { int xx = P[p].x + dx[i], yy = P[p].y + dy[i], tem; if(xx < 1 || xx > C || yy < 1 || yy > R) continue; if(tem = Check(xx, yy)) { if(Color[tem] == 2) add(tem, a, INF); else add(c, tem, INF); } } } int main() { C = read(), R = read(); swap(C, R); n = read(); S = 0, T = 2 * n + 100; for(int i = 1; i <= n; i ++) { int x = read(), y = read(), w = read(); swap(x, y); if((x & 1) && ((!((y - 1) % 4)) || (!((y - 2) % 4)))) { P[++ cnt].x = x, P[cnt].y = y; P[cnt].w = w; continue; } if((!(x & 1)) && ((!((y - 3) % 4)) || (!((y - 4) % 4)))) { P[++ cnt].x = x, P[cnt].y = y; P[cnt].w = w; continue; } if(x & 1) { if(((y - 3) % 4)) add(S, id(x, y), w), Color[id(x, y)] = 2; else add(id(x, y), T, w), Color[id(x, y)] = 1; } else { if(!((y - 2) % 4)) add(id(x, y), T, w), Color[id(x, y)] = 1; else add(S, id(x, y), w), Color[id(x, y)] = 2; } } sort(P + 1, P + 1 + cnt, cmp); for(int i = 1; i <= cnt; i += 1) { if(P[i + 1].x != P[i].x) continue; if(P[i + 1].y != P[i].y + 1) continue; Get_Graph(i); } printf("%d\n", Dinic()); return 0; }
原文:https://www.cnblogs.com/twilight-sx/p/9189329.html