標籤:bzoj 最小割 最大流 網路流
題目大意
給出一個N×N×N的矩陣,矩陣上的每一個方塊可以塗上兩種顏色,相鄰的兩個方塊如果塗上了不同的顏色,就會產生一點能量。現在已知了一些方塊的顏色,問最多可以產生多少點能量。
思路
假設所有相鄰的方塊之間全部都產生能量,且不考慮已經上好色的方塊,之後減去不合法的就行了。
一般來說這種相鄰的方塊之間會產生一些什麼的一般都是把所有點染色,一種顏色的與S相連,另一種與T相連。這道題中,左側的點若是最終屬於S集,代表這個點上了P色,否則上了N色;右側的點若是最終屬於T集,代表這個點上了P色,否則上了N色。在最後的答案中,我們只記錄P色周圍有多少N色,而不討論N色周圍有多少P色。
因為現在我們假設的是所有的塊之間都產生能量,所以先從S到所有左側的點連上流量為這個點周圍相鄰的點的個數,右側的點同理。如果一個點並不是P色,那麼它就要失去所有的能量(因為只記錄P色產生的能量)。
如果左邊的點和右邊的點不在同一個集合的話(也就是它們之間的邊被割開了),那麼他們最後的顏色就是相同的,因此不會產生能量,左側和右側各減1,所以這個邊的流量就是2。
之後跑個Dinic就行了。。。
CODE
#define _CRT_SECURE_NO_WARNINGS#include <queue>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define MAX 50#define MAXP (MAX * MAX * MAX)#define MAXE 1000010#define INF 0x3f3f3f3f#define S 0#define T (MAXP - 1)using namespace std;#define INRANGE(x, y, z) (x * y * z && x <= m && y <= m && z <= m)const int dx[] = {0, 1, -1, 0, 0, 0, 0};const int dy[] = {0, 0, 0, 1, -1, 0, 0};const int dz[] = {0, 0, 0, 0, 0, 1, -1};struct MaxFlow{ int head[MAXP], total; int next[MAXE], aim[MAXE], flow[MAXE]; int deep[MAXP]; MaxFlow():total(1) {} void Add(int x, int y, int f) { next[++total] = head[x]; aim[total] = y; flow[total] = f; head[x] = total; } void Insert(int x, int y, int f) { Add(x, y, f); Add(y, x, 0); } bool BFS() { static queue<int> q; while(!q.empty()) q.pop(); memset(deep, 0, sizeof(deep)); deep[S] = 1; q.push(S); while(!q.empty()) { int x = q.front(); q.pop(); for(int i = head[x]; i; i = next[i]) if(flow[i] && !deep[aim[i]]) { deep[aim[i]] = deep[x] + 1; q.push(aim[i]); if(aim[i] == T) return true; } } return false; } int Dinic(int x, int f) { if(x == T) return f; int temp = f; for(int i = head[x]; i; i = next[i]) if(flow[i] && deep[aim[i]] == deep[x] + 1 && temp) { int away = Dinic(aim[i], min(flow[i], temp)); if(!away) deep[aim[i]] = 0; flow[i] -= away; flow[i^1] += away; temp -= away; } return f - temp; }}solver;int m;char src[MAX][MAX][MAX];int num[MAX][MAX][MAX], cnt;int ans;int main(){ cin >> m; for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) scanf("%s", src[i][j] + 1); for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) for(int k = 1; k <= m; ++k) num[i][j][k] = ++cnt; for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) for(int k = 1; k <= m; ++k) { int near = 0; for(int d = 1; d <= 6; ++d) { int fx = i + dx[d]; int fy = j + dy[d]; int fz = k + dz[d]; bool flag = INRANGE(fx, fy, fz); near += flag; if(flag && (i + j + k)&1) solver.Insert(num[i][j][k], num[fx][fy][fz], 2); } ans += near; if((i + j + k)&1) solver.Insert(S, num[i][j][k], near); else solver.Insert(num[i][j][k], T, near); } for(int i = 1; i <= m; ++i) for(int j = 1; j <= m; ++j) for(int k = 1; k <= m; ++k) if((i + j + k)&1) { if(src[i][j][k] == ‘P‘) solver.Insert(S, num[i][j][k], INF); else if(src[i][j][k] == ‘N‘) solver.Insert(num[i][j][k], T, INF); } else { if(src[i][j][k] == ‘P‘) solver.Insert(num[i][j][k], T, INF); else if(src[i][j][k] == ‘N‘) solver.Insert(S, num[i][j][k], INF); } int max_flow = 0; while(solver.BFS()) max_flow += solver.Dinic(S, INF); cout << ans - max_flow << endl; return 0;}
BZOJ 1976 BeiJing2010組隊 能量魔方 Cube 最小割