洛谷P3159 交換棋子 神奇的網路流
阿新 • • 發佈:2018-12-13
神奇的建模。。。原題連結
如果你真的把交換看成交換,就\(GG\)了。首先我們要把交換看成是白棋的移動。
然後,很容易的就想到建模的大致思路:建立超級源點S和超級匯點T,從S向初始局面每個白棋所在的格子連邊,從目標局面每個白棋所在的格子向T連邊,在相鄰的格子中間加一些有限制的邊,跑一波費用流。
那中間的那些邊應該怎麼加呢,先考慮把每個格子拆成兩個點,中間連一條以交換次數為流量上限的邊。但是這樣會有問題,考慮某個白棋移動路徑上的點,其實只有開頭的格子交換了一次,中間的都是兩次。只拆兩個體現不了在中間或首尾的差別,那我們就拆三個。
把一個格子拆成三個點:TYPE1,TYPE2,TYPE3,分別表示流入,當前格子,流出。與S
那這些邊的容量和費用該怎麼設定呢?
1.TYPE1與TYPE3之間的連邊容量為\(INF\),費用為\(1\)。
2.S與TYPE2,TYPE2與T容量為\(1\),費用為\(0\)。
3.TYPE2與TYPE1和TYPE3的連邊(重點):
對於某個格子(假設它的交換上限為\(w\),"/"號代表整除):
①若初始為白棋,目標為黑棋,則從TYPE1向TYPE2連一條容量為\(w/2\),費用為\(0\)的邊,從TYPE2向TYPE3連一條容量為\((w+1)/2\)
②若初始為黑棋,目標為白棋,則從TYPE1向TYPE2連一條容量為\((w+1)/2\),費用為\(0\)的邊,從TYPE2向TYPE3連一條容量為\(w/2\),費用為\(0\)的邊
③若始末顏色相同,則從TYPE1向TYPE2連一條容量為\(w/2\),費用為\(0\)的邊,從TYPE2向TYPE3連一條容量為\(w/2\),費用為\(0\)的邊
為什麼權值要這樣設,因為我們要保持收支平衡,使得每個格子在交換之後的顏色是對的。還有我們考慮的是白棋的移動,因此①應該會多一次流出,於是我們儘量把零頭分給TYPE2與TYPE3之間的那條邊。②同理。
注意判\(-1\)!
#include <bits/stdc++.h> using namespace std; #define pii pair<int, int> #define mp make_pair #define pb push_back #define N 10000 #define INF 0x3f3f3f3f int n, m, S, T; struct MCMF { struct Edge { int from, to, cap, flow, cost; }; int S, T; int d[N+5], a[N+5], vis[N+5], pre[N+5]; vector<int> G[N+5]; vector<Edge> edges; void init(int S, int T) { this->S = S, this->T = T; } void addEdge(int from, int to, int cap, int cost) { edges.pb(Edge{from, to, cap, 0, cost}), edges.pb(Edge{to, from, 0, 0, -cost}); G[from].pb(edges.size()-2), G[to].pb(edges.size()-1); } int SPFA(int &flow, int &cost) { memset(d, 0x3f, sizeof d), memset(vis, 0, sizeof vis); d[S] = 0, a[S] = INF, vis[S] = 1, pre[S] = 0; queue<int> q; q.push(S); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = 0; i < G[u].size(); ++i) { Edge &e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] > d[u]+e.cost) { d[e.to] = d[u]+e.cost; pre[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap-e.flow); if(!vis[e.to]) vis[e.to] = 1, q.push(e.to); } } } if(d[T] == INF) return 0; int u = T; flow += a[T], cost += d[T]*a[T]; while(u != S) { edges[pre[u]].flow += a[T], edges[pre[u]^1].flow -= a[T]; u = edges[pre[u]].from; } return 1; } pii minCost() { int flow = 0, cost = 0; while(SPFA(flow, cost)); return mp(flow, cost); } }solver; int Val(char c) { return c-'0'; } int TYPE1(int i, int j) { return ((i-1)*m+j)*3-2; } int TYPE2(int i, int j) { return ((i-1)*m+j)*3-1; } int TYPE3(int i, int j) { return ((i-1)*m+j)*3; } int a[25][25], b[25][25], w[25][25]; int d[8][2] = {-1,0,1,0,0,-1,0,1,-1,-1,-1,1,1,-1,1,1}; //0:black 1:white int main() { scanf("%d%d", &n, &m); S = 0, T = 3*n*m+1; solver.init(S, T); char c; int sum = 0; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) cin >> c, a[i][j] = Val(c), sum += a[i][j]; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) cin >> c, b[i][j] = Val(c); for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) cin >> c, w[i][j] = Val(c); for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { if(a[i][j]) solver.addEdge(S, TYPE2(i, j), 1, 0); if(b[i][j]) solver.addEdge(TYPE2(i, j), T, 1, 0); if(a[i][j] == 0 && b[i][j] == 1) solver.addEdge(TYPE1(i, j), TYPE2(i, j), (w[i][j]+1)/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0); else if(a[i][j] == 1 && b[i][j] == 0) solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), (w[i][j]+1)/2, 0); else solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0); for(int k = 0, ti, tj; k < 8; ++k) { ti = i+d[k][0], tj = j+d[k][1]; if(ti < 1 || ti > n || tj < 1 || tj > m) continue; solver.addEdge(TYPE3(i, j), TYPE1(ti, tj), INF, 1); } } pii res = solver.minCost(); if(res.first != sum) printf("-1\n"); else printf("%d\n", res.second); return 0; }