POJ 2175 spfa费用流消圈
来源:互联网 发布:众安保险尊享e生 知乎 编辑:程序博客网 时间:2024/06/05 19:23
题意:给出n栋房子位置和每栋房子里面的人数,m个避难所位置和每个避难所可容纳人数。然后给出一个方案,判断该方案是否最优,如果不是求出一个更优的方案。
思路:很容易想到用最小费用流求出最优时间,在与原方案花费时间对比判断原方案是否最优。也许是组数太多了,这种方法会超时的。 放弃该思路。
看看题目没要求要最优解,而是得到一个更优的解。
在原图的所有反向边中能够找到一个总费用为负的回路(而且要有流量)的话,那就该解不是最优解,把该负环消去,更新流量,得到优化后的解。(原因: 反向边保存的是已经流过的流量, 如果出现环,那么说明我们可以不走这个环,那么总的费用就变小了)。
具体操作:从汇点出发SPFA,一个点入队次数大于顶点数时就可以判断有负圈存在了。
特别注意:但这时第一次入队n次的这个点却未必是负圈上的。
如以下数据
<u, v, w>
<1, 2, 4>
<2, 3, -3>
<3, 4, -2>
<4, 5, -2>
<5, 6, -2>
<6, 3, -2>
我们从1出发,找到的点是2, 2不在负环上
如何找到负圈上的点和负圈:
我们可以记录下来每个点被更新的前一个点,沿这个路径不停地往回找,直到发现找到的这个点在之间已经遇到过了,那么找到的这个点就一定是某个负圈上的点了。最后以这个点为基础,回溯找到整个负圈并更新流量即可。
#include <cstdio>#include <cstring>#include <algorithm>#include <queue>using namespace std;const int maxn = 303;const int inf = 1e9;struct node {int x, y, c;void in() {scanf("%d%d%d", &x, &y, &c);}} a[maxn], b[maxn];int sa[maxn], sb[maxn];int n, m;struct Edge {int u, v, c, w, next;Edge(int u, int v, int c, int w, int next) :u(u), v(v), c(c), w(w), next(next) {}Edge() {}} edge[1000006];int head[maxn], E;void init() {memset(head, -1, sizeof(head));E = 0;}void add(int s, int t, int c, int cc, int w) {edge[E] = Edge(s, t, c, w, head[s]);head[s] = E++;edge[E] = Edge(t, s, cc, -w, head[t]);head[t] = E++;}inline int F(int x) {return x > 0 ? x : -x;}inline int Dis(int i, int j) {return F(a[i].x - b[j].x) + F(a[i].y - b[j].y) + 1;}int S, T;bool vis[maxn];int dis[maxn], in[maxn], pre[maxn];int spfa(int s, int n) {//消负环int i, u, v;for(i = 0; i <= n; i++)dis[i] = inf, pre[i] = -1, vis[i] = 0, in[i] = 0;queue <int> q;dis[s] = 0;vis[s] = 1;in[s]++;q.push(s);while(!q.empty()) {u = q.front();q.pop();vis[u] = 0;for(i = head[u]; i != -1; i = edge[i].next) {v = edge[i].v;if(edge[i].c && dis[v] > dis[u] + edge[i].w) {dis[v] = dis[u] + edge[i].w;pre[v] = i;if(!vis[v]) {vis[v] = 1;q.push(v);in[v]++;if(in[v] >= n)return v;}}}}return -1;}void update(int p) {int u = pre[p], i;int aug = inf;aug = min(aug, edge[u].c);for(i = pre[edge[u].u]; i != u; i = pre[edge[i].u])aug = min(aug, edge[i].c);edge[u].c -= aug;edge[u ^ 1].c += aug;for(i = pre[edge[u].u]; i != u; i = pre[edge[i].u]) {edge[i].c -= aug;edge[i ^ 1].c += aug;}}void solve() {int p = spfa(T, T+1); //int i, j;if(p == -1) {printf("OPTIMAL\n");return;}printf("SUBOPTIMAL\n");memset(vis, 0, sizeof(vis));while(!vis[p]) {vis[p] = 1;p = edge[pre[p]].u;}update(p);for(i = 0; i < n; i++) {for(j = 0; j < m-1; j++)printf("%d ", edge[(i * m + j)<<1^1].c);printf("%d\n", edge[(i * m + j)<<1^1].c);}}int main() {int i, j, z;while(~scanf("%d%d", &n, &m)) {S = n + m;T = S + 1;init();for(i = 0; i < n; i++)a[i].in(), sa[i] = 0;for(i = 0; i < m; i++)b[i].in(), sb[i] = 0;//构造流完题目中可行流的残余网络for(i = 0; i < n; i++)for(j = 0; j < m; j++) {scanf("%d", &z);add(i, j + n, inf, z, Dis(i, j));sa[i] += z;sb[j] += z;}for(i = 0; i < n; i++)add(S, i, a[i].c - sa[i], sa[i], 0);for(i = 0; i < m; i++)add(i + n, T, b[i].c - sb[i], sb[i], 0);solve();}return 0;}
- POJ 2175 spfa费用流消圈
- Evacuation Plan (poj 2175 SPFA费用流消圈)
- POJ 2135 费用流 spfa
- POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)
- poj 2195 最小费用最大流 EK+SPFA
- poj 2135 最小费用最大流 EK+SPFA
- POJ 2195:Going Home(SPFA最小费用最大流)
- poj 2516 Minimum(SPFA解决最小费用最大流)
- POJ 2516 Minimum Cost (最小费用流 SPFA)
- 【费用流消圈】POJ-2175 Evacuation Plan
- POJ 2175 Evacuation Plan 费用流消圈
- POJ 2175 Evacuation Plan 费用流消圈算法
- POJ 2175 Evacuation Plan 费用流消圈
- 费用流SPFA版
- SPFA费用流模板
- poj 2175(费用流判负环+消环)
- poj 2195 Going Home--最小费用最大流--spfa--动态数组--或者用 最小权匹配
- poj 1724 spfa(有费用上限的最短路(双重标准最短路))
- 8.4 内部排序法---归并类排序
- hdu4708
- 深度优先搜索
- 竖直滑动的viewpager-------VerticalAdapter
- requestDisallowInterceptTouchEvent
- POJ 2175 spfa费用流消圈
- MapReduce调度与执行原理之作业初始化
- 自我工作中常用的Linux命令记录
- JAVA String.format 方法使用介绍
- ORACLE FLASHBACK DROP 知识整理
- Android 浏览器自定义scheme:market://协议
- 文本相似度算法(余弦定理)
- gdb调试器的常用命令
- python中用正则表达式检测邮件是否合法