loj2321. 「清華集訓 2017」無限之環
阿新 • • 發佈:2020-07-10
題目:https://loj.ac/problem/2321
簡明題意:給你一個矩陣,每個格子都有一種水管,每種水管可以向上下左右這四個方向中的若干個給定的方向延伸,每次可以將一個格子的水管順或逆時針旋轉90°,問使水管全部閉合的最小操作次數
看起來就非常的可以最小費用最大流
既然是矩陣,那麼肯定要黑白染色,然後我們考慮拆點,每個格子多拆四個方向的點,這樣可以表示出來現在水管的狀態,對於旋轉可以分情況討論
- 只有一個方向的點:該方向和和它相對的方向連費用為2的邊,說明需要旋轉2次;剩下的兩個方向連費用為1的邊,只需要旋轉1次
- 有兩個方向的邊:這兩個方向分別和和該方向相對的邊連費用為1的邊,旋轉1次的可以表示;旋轉2次的可以通過都流這兩個邊表示
- 有三個方向的邊:將有相對邊的邊和對邊連費用為2的邊,說明需要旋轉2次;剩下的兩個邊向沒有方向的邊連費用為1的邊,只需要旋轉1次
- 四個方向的邊不需要旋轉,只有對著的方向的邊不能旋轉
然後我們記錄下所有格子方向的個數和\(tot\),\(tot/2=flow\)說明有界,否則無解
剛才的連邊過程實際上是把旋轉中重合的邊略去了,畫畫圖就可以很好理解了
我是分情況討論的,沒有用也不會用什麼高超的位運算技巧QAQ
Code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> const int N = 2e3; const int M = 114514; const int inf = 19260817; using namespace std; struct edges { int to,cost,f; }edge[M * 2 + 5]; int nc,tot,num[M + 5],mf,p[M + 5],n,m,a[N + 5],ans,mp[N + 5][N + 5],id[N + 5][N + 5],id_cnt,co[N + 5][N + 5],S,T,nxt[M + 5],head[M + 5],edge_cnt = 1,dis[M + 5],vis[M + 5],q[M + 5],cur[M + 5]; void add_edge(int u,int v,int w,int f) { edge[++edge_cnt] = (edges){v,w,f}; nxt[edge_cnt] = head[u]; head[u] = edge_cnt; } void add(int u,int v,int w,int f) { if (!nc) swap(u,v); add_edge(u,v,w,f); add_edge(v,u,0,-f); } void add_chai(int S,int x) { add(S,x * 5,inf,0); if (a[x] & 1) add(x * 5,x * 5 + 1,1,0); if (a[x] & 2) add(x * 5,x * 5 + 2,1,0); if (a[x] & 4) add(x * 5,x * 5 + 3,1,0); if (a[x] & 8) add(x * 5,x * 5 + 4,1,0); } void rev(int x) { if (a[x] == 1) add(x * 5 + 1,x * 5 + 2,1,1),add(x * 5 + 1,x * 5 + 4,1,1),add(x * 5 + 1,x * 5 + 3,1,2); else if (a[x] == 2) add(x * 5 + 2,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 4,1,2); else if (a[x] == 3) add(x * 5 + 1,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 4,1,1); else if (a[x] == 4) add(x * 5 + 3,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 4,1,1),add(x * 5 + 3,x * 5 + 1,1,2); else if (a[x] == 6) add(x * 5 + 3,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 4,1,1); else if (a[x] == 7) add(x * 5 + 2,x * 5 + 4,1,2),add(x * 5 + 1,x * 5 + 4,1,1),add(x * 5 + 3,x * 5 + 4,1,1); else if (a[x] == 8) add(x * 5 + 4,x * 5 + 1,1,1),add(x * 5 + 4,x * 5 + 3,1,1),add(x * 5 + 4,x * 5 + 2,1,2); else if (a[x] == 9) add(x * 5 + 1,x * 5 + 3,1,1),add(x * 5 + 4,x * 5 + 2,1,1); else if (a[x] == 11) add(x * 5 + 1,x * 5 + 3,1,2),add(x * 5 + 4,x * 5 + 3,1,1),add(x * 5 + 2,x * 5 + 3,1,1); else if (a[x] == 12) add(x * 5 + 4,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 1,1,1); else if (a[x] == 13) add(x * 5 + 4,x * 5 + 2,1,2),add(x * 5 + 1,x * 5 + 2,1,1),add(x * 5 + 3,x * 5 + 2,1,1); else if (a[x] == 14) add(x * 5 + 3,x * 5 + 1,1,2),add(x * 5 + 4,x * 5 + 1,1,1),add(x * 5 + 2,x * 5 + 1,1,1); } int dfs(int u,int flow) { if (u == T) return flow; int sum = 0; p[u] = 1; for (int &i = cur[u];i;i = nxt[i]) { int v = edge[i].to,w = edge[i].cost,f = edge[i].f; if (w && dis[u] + f == dis[v] && !p[v]) { int res = dfs(v,min(flow,w)); edge[i].cost -= res; edge[i ^ 1].cost += res; flow -= res; sum += res; mf += res * f; if (!flow) break; } } return sum; } int spfa() { for (int i = 1;i <= id_cnt * 5 + 4;i++) cur[i] = head[i],vis[i] = 0,dis[i] = inf,p[i] = 0; dis[S] = 0; vis[S] = 1; int l = 1,r = 0; q[++r] = S; while (l <= r) { int u = q[l++]; vis[u] = 0; for (int i = head[u];i;i = nxt[i]) { int v = edge[i].to,w = edge[i].cost,f = edge[i].f; if (w && dis[u] + f < dis[v]) { dis[v] = dis[u] + f; if (!vis[v]) { q[++r] = v; vis[v] = 1; } } } } return dis[T]; } int main() { scanf("%d%d",&n,&m); for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) id[i][j] = ++id_cnt,co[i][j] = (i + j) % 2; for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) scanf("%d",&a[id[i][j]]); for (int i = 1;i <= 15;i++) for (int j = 0;j < 4;j++) if (i & (1 << j)) num[i]++; S = 1; T = 2; for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) { nc = co[i][j]; if (co[i][j] == 1) add_chai(S,id[i][j]); else add_chai(T,id[i][j]); tot += num[a[id[i][j]]]; rev(id[i][j]); if (id[i - 1][j]) add(id[i][j] * 5 + 1,id[i - 1][j] * 5 + 3,1,0); if (id[i][j - 1]) add(id[i][j] * 5 + 4,id[i][j - 1] * 5 + 2,1,0); } int ans = 0; while (spfa() != inf) ans += dfs(S,inf); if (tot / 2 == ans) printf("%d\n",mf); else printf("-1\n"); return 0; }