传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1976
此题真是神来一割。。。
首先很容易看出它是一个二分图的模型,但连边实在很恼火,感觉无论怎么连都剪不断理还乱的,于是终于膜拜了题解。
这道题关键在于转化,何不让所有点都加上它的最大值,然后选择一个方案令它减去的最少呢,于是我们想到了最小割。
具体讲解可以点这里。我只想总结几点:
1.最小割往往是解决这类二分图问题的神器,但它求的是最小方案,有时需要转化一下
2.在有些已经确定的点上,我们可以连容量为INF的边来保证它不被割掉
3.从源点到汇点的每一条边都代表一种方案,要仔细分析
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define INF 0x3f3f3f3f const int maxn = 64010, maxm = 1000010, maxk = 45; int fir[maxn], edge[maxm], next[maxm], cap[maxm]; int d[maxn], vd[maxn]; int dd[6][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {-1, 0, 0}, {0, -1, 0}, {0, 0, -1}}; int num[maxk][maxk][maxk]; int vis[maxk][maxk][maxk]; int n, ans, s, t, cnt, tot; void addedge(int a, int b, int f) { edge[cnt] = b; next[cnt] = fir[a]; fir[a] = cnt; cap[cnt ++] = f; edge[cnt] = a; next[cnt] = fir[b]; fir[b] = cnt; cap[cnt ++] = 0; return; } bool check(int x, int y, int z) { if(x < 1 || y < 1 || z < 1 || x > n || y > n || z > n) return false; return true; } int Isap(int r, int aflow) { if(r == t) return aflow; int flow = aflow, f, md = t - 1; for(int i = fir[r]; ~i ; i = next[i]) { int v = edge[i]; if(cap[i]) { if(d[r] == d[v] + 1) { f = min(flow, cap[i]); f = Isap(v, f); cap[i] -= f; cap[i ^ 1] += f; flow -= f; if(d[s] >= t) return aflow - flow; if(!flow) break; } md = min(md, d[v]); } } if(flow == aflow) { vd[d[r]] --; if(!vd[d[r]]) d[s] = t; d[r] = md + 1; vd[d[r]] ++; } return aflow - flow; } int calc(char c) { if(c == '?') return 0; if(c == 'P') return 1; if(c == 'N') return 2; } int main() { memset(fir, -1, sizeof(fir)); scanf("%d", &n); s = n * n * n + 1; t = s + 1; char c; for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { for(int k = 1; k <= n; k ++) { c = getchar(); while(c != '?' && c != 'P' && c != 'N') c = getchar(); vis[i][j][k] = calc(c); num[i][j][k] = ++tot; } } } for(int i = 1; i <= n; i ++) { for(int j = 1; j <= n; j ++) { for(int k = 1; k <= n; k ++) { int res = 0; if((i + j + k) & 1) { for(int x = 0; x < 6; x ++) { int tx = i + dd[x][0]; int ty = j + dd[x][1]; int tz = k + dd[x][2]; if(check(tx, ty, tz)) { addedge(num[i][j][k], num[tx][ty][tz], 2); res ++; } } addedge(s, num[i][j][k], res); if(vis[i][j][k] == 1) addedge(s, num[i][j][k], INF); else if(vis[i][j][k] == 2) addedge(num[i][j][k], t, INF); } else { for(int x = 0; x < 6; x ++) { int tx = i + dd[x][0]; int ty = j + dd[x][1]; int tz = k + dd[x][2]; if(check(tx, ty, tz)) { res ++; } } addedge(num[i][j][k], t, res); if(vis[i][j][k] == 2) addedge(s, num[i][j][k], INF); else if(vis[i][j][k] == 1) addedge(num[i][j][k], t, INF); } ans += res; } } } vd[0] = t; while(d[s] < t) ans -= Isap(s, INF); printf("%d\n", ans); return 0; }