改进: 增加附加弧(s, t), 费用大于s-t最大费用路(如VC), 弧上的初始流大于s-t最大流(如s出发的弧容量和加1)消圈算法将让尽量多的流移出附加弧, 因此得到的流是最大流. 最后附加弧可能有余量,应当忽略
实际效果: 最小费用增广路算法, 因为Gf中任何s-t路和t-s一定构成负费用圈
最坏情况分析: 每次找负费用圈O(VE), 每次只把费用更新1, 一共需要ECM次(M为最大费用, C为容量), 在稀疏图中总时间复杂度为O(VE^2CM)=O(V^3CM)用途:由于效率极低,因此消圈算法一般用于判断是否为最优解,而不用于求最优解
定理:一个费用流是最小费用流的充要条件是这个费用流的残量网络没有负费用圈。
poj 2175 Evacuation Plan特别注意:如果某个点v入队次数大于点数,不能说明v是环内的点,但v的某个前驱一定是环内的点。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.StreamTokenizer; import java.util.Arrays; public class Evacation2175 { int maxn = 210, inf = 1 << 28; class MINCOST { int E[][] = new int[maxn][maxn], p[] = new int[maxn], n; int queue[] = new int[maxn], d[] = new int[maxn]; void init(int n) { this.n = n; for (int i = 0; i <= n; i++) Arrays.fill(E[i], inf); } boolean vis[] = new boolean[maxn]; int cnt[] = new int[maxn]; int spfa(int s, int t) { Arrays.fill(d, inf); Arrays.fill(p, -1); Arrays.fill(vis, false); d[s] = 0; vis[s] = true; int head = 0, tail = 0, v, u; queue[(tail++) % maxn] = s; while (tail != head) { u = queue[(head++) % maxn]; vis[u] = false; for (v = 0; v < n; v++) { if (E[u][v] == inf) continue; if (d[u] + E[u][v] < d[v]) { d[v] = d[u] + E[u][v]; p[v] = u; if (!vis[v]) { queue[(tail++) % maxn] = v; vis[v] = true; if (++cnt[v] > n) return v; }// 入队 }// 松弛 }// 邻接边 }// while return -1; } boolean solve(int s, int t, int nt) { int idx = spfa(t, s); if (idx == -1) return true; else { int v = idx; Arrays.fill(vis, false); while (!vis[v]) { vis[v] = true; v = p[v]; } idx = v; do { if (v > nt && p[v] <= nt) ans[p[v]][v - nt]++; if (v <= nt && p[v] > nt) ans[v][p[v] - nt]--; v = p[v]; } while (v != idx); return false; } } } int dis(int i, int j) { return Math.abs(x[i] - x[j]) + Math.abs(y[i] - y[j]) + 1; } MINCOST mc = new MINCOST(); int x[] = new int[maxn], y[] = new int[maxn], cap[] = new int[maxn]; int ans[][] = new int[maxn][maxn], n, m; void init() throws IOException { n = nextInt(); m = nextInt(); for (int i = 1; i <= n; i++) { x[i] = nextInt(); y[i] = nextInt(); cap[i] = nextInt(); } for (int i = 1; i <= m; i++) { x[i + n] = nextInt(); y[i + n] = nextInt(); cap[i] = nextInt(); } } int sum[] = new int[maxn]; void build() throws IOException { mc.init(m + n + 2); Arrays.fill(sum, 0); for (int i = 1; i <= n; i++) mc.E[i][0] = 0; for (int i = 1; i <= m; i++) mc.E[m + n + 1][n + i] = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { ans[i][j] = nextInt(); mc.E[i][j + n] = dis(i, j + n); if (ans[i][j] > 0) mc.E[j + n][i] = -dis(i, j + n); sum[j] += ans[i][j]; } for (int i = 1; i <= m; i++) { if (sum[i] > 0) mc.E[m + n + 1][i + n] = 0; if (sum[i] < cap[i]) mc.E[i + n][m + n + 1] = 0; } } void run() throws IOException { init(); build(); if (mc.solve(0, m + n + 1, n)) System.out.println("OPTIMAL"); else { System.out.println("SUBOPTIMAL"); for (int i = 1; i <= n; i++) { for (int j = 1; j < m; j++) System.out.print(ans[i][j] + " "); System.out.println(ans[i][m]); } } } StreamTokenizer in = new StreamTokenizer(new BufferedReader( new InputStreamReader(System.in))); int nextInt() throws IOException { in.nextToken(); return (int) in.nval; } public static void main(String[] args) throws IOException { new Evacation2175().run(); } }