消圈法解最小费用流(poj 2175 Evacuation Plan)

来源:互联网 发布:苹果mac怎么切换系统 编辑:程序博客网 时间:2024/05/29 02:26
消圈算法: 先求最大流, 再在Gf中寻找负费用圈并沿它增广

改进: 增加附加弧(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
题意:有n个建筑物,m个避难所,已知每个建筑物的坐标和拥有的人及每个避难所的坐标和容量。建筑物i和避难所j的距离为|xi-xj| + |yi-yj| + 1。现在要求把建筑物的人都疏散到避难所。现给出一个方案:建筑物i有x[i][j]的人去j避难所,且这个方案保证所有人都找到了避难所。问这个方案是不是总距离最小的,如果不是输出一个比给定方案更好好的方案即可(SPJ)
解法:观察残余网络,源点到建筑之间肯定满流,因此只需建一条i->s的长度为0的边;
对于任一对建筑和避难所的点对(i,j),由于原图中i->j流量为正无穷,因此加一条长度为两点间距离的边;
如果原方案中i->j有流量,则j->i加一条长度为两点间负距离的边;
对于每一个避难所j,所过方案中j的和大于0,则建一条从t->j的长度为0的边;如果和小于j的容量,则建一条j->i的长度为0的边。
以t为源点进行一次spfa,若发现环则将环内正向边流量+1,反向边流量-1,这样得到的解一定比原方案优;

特别注意:如果某个点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;}// 入队}// 松弛}// 邻接边}// whilereturn -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();}}


原创粉丝点击