POJ 2175 Evacuation Plan 费用流 负圈定理
题目给了一个满足最大流的残量网络,判断是否费用最小。
如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了。
1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在负环上。
2.这个负环可能包括汇点t,所以构建残量网络的时候也要考虑防空洞到t上的容量。
//#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<iostream> #include<sstream> #include<cmath> #include<climits> #include<string> #include<map> #include<queue> #include<vector> #include<stack> #include<set> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> pii; #define pb(a) push(a) #define INF 0x1f1f1f1f #define lson idx<<1,l,mid #define rson idx<<1|1,mid+1,r #define PI 3.1415926535898 template<class T> T min(const T& a,const T& b,const T& c) { return min(min(a,b),min(a,c)); } template<class T> T max(const T& a,const T& b,const T& c) { return max(max(a,b),max(a,c)); } void debug() { #ifdef ONLINE_JUDGE #else freopen("in.txt","r",stdin); // freopen("d:\\out1.txt","w",stdout); #endif } int getch() { int ch; while((ch=getchar())!=EOF) { if(ch!=' '&&ch!='\n')return ch; } return EOF; } const int maxn = 110; int X[maxn], Y[maxn], B[maxn]; int P[maxn], Q[maxn], C[maxn]; int E[maxn][maxn]; int N, M; bool input() { if(scanf("%d%d", &N, &M) == EOF) return false; for(int i = 1; i <= N; i++) scanf("%d%d%d", &X[i], &Y[i], &B[i]); for(int i = 1; i <= M; i++) scanf("%d%d%d", &P[i], &Q[i], &C[i]); for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) scanf("%d", &E[i][j]); return true; } struct Edge { int from, to, cost, cap; }; const int maxv = maxn * 2; int n,s,t; vector<int> g[maxv]; vector<Edge> edge; int road[maxv]; int d[maxv]; int inq[maxv]; int vcnt[maxv]; int SPFA() { queue<int> q; memset(d, INF, sizeof(d)); memset(inq, false, sizeof(inq)); memset(vcnt, 0, sizeof(vcnt)); d[s] = 0; road[s] = -1; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); inq[u] = false; for(int i = 0; i < g[u].size(); i++) { Edge &e = edge[g[u][i]]; if(e.cap > 0 && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; road[e.to] = g[u][i]; if(!inq[e.to]) { inq[e.to] = true; if(++vcnt[e.to] > n) return e.to; q.push(e.to); } } } } return -1; } void add(int from, int to, int cost, int cap, int flow) { edge.push_back((Edge){from, to, cost, cap - flow}); g[from].push_back(edge.size() - 1); edge.push_back((Edge){to, from, -cost, flow}); g[to].push_back(edge.size() - 1); } int Cost(int i, int j) { return abs(X[i] - P[j]) + abs(Y[i] - Q[j]) + 1; } void init() { for(int i = 1; i <= n; i++) g[i].clear(); edge.clear(); } void construct() { n = N + M + 2; s = n - 1; t = n; init(); int sum[maxn] = {0}; for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) sum[j] += E[i][j]; for(int i = 1; i <= N; i++) add(s, i, 0, B[i], 0); for(int j = 1; j <= M; j++) add(j + N, t, 0, C[j], sum[j]); for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) add(i, j + N, Cost(i, j), min(B[i], C[i]), E[i][j]); } int vis[maxv]; void solve() { int u = SPFA(); if(u == -1) { printf("OPTIMAL\n"); return ; } memset(vis, 0, sizeof(vis)); while(1) { if(vis[u]) break; vis[u] = true; u = edge[road[u]].from; } memset(vis, 0, sizeof(vis)); for(int e = road[u]; !vis[edge[e].to]; e = road[edge[e].from]) { int x = edge[e].from; int y = edge[e].to; vis[y] = true; if(x == t || y == t) continue; if(x < y) E[x][y - N] += 1; else E[y][x - N] -= 1; } printf("SUBOPTIMAL\n"); for(int i = 1; i <= N; i++) for(int j = 1; j <= M; j++) printf("%d%c", E[i][j], j == M? '\n': ' '); } int main() { debug(); while(input()) { construct(); solve(); } return 0; }