POJ 2175 spfa费用流消圈
题意:给出n栋房子位置和每栋房子里面的人数,m个避难所位置和每个避难所可容纳人数。然后给出一个方案,判断该方案是否最优,如果不是求出一个更优的方案。
思路:很容易想到用最小费用流求出最优时间,在与原方案花费时间对比判断原方案是否最优。也许是组数太多了,这种方法会超时的。 放弃该思路。
看看题目没要求要最优解,而是得到一个更优的解。
在原图的所有反向边中能够找到一个总费用为负的回路(而且要有流量)的话,那就该解不是最优解,把该负环消去,更新流量,得到优化后的解。(原因: 反向边保存的是已经流过的流量, 如果出现环,那么说明我们可以不走这个环,那么总的费用就变小了)。
具体操作:从汇点出发SPFA,一个点入队次数大于顶点数时就可以判断有负圈存在了。
特别注意:但这时第一次入队n次的这个点却未必是负圈上的。
如数据
1 2 w = 5
2 3 w = -1
3 4 w = -1
4 2 w = -1
我们从点1出发, 1->2->3->4->2, 到了2以后,由于存在反向边(2 1 w = -5),走反向边。
路径为 1->2->3->4->2-> 1->2->3->4, 所以最后第一次入队n次的点是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费用流消圈的更多相关文章
- POJ 2175:Evacuation Plan(费用流消圈算法)***
http://poj.org/problem?id=2175 题意:有n个楼,m个防空洞,每个楼有一个坐标和一个人数B,每个防空洞有一个坐标和容纳量C,从楼到防空洞需要的时间是其曼哈顿距离+1,现在给 ...
- poj 2175 费用流消圈
题意抽象出来就是给了一个费用流的残存网络,判断该方案是不是最优方案,如果不是,还要求给出一个更优方案. 在给定残存网络上检查是否存在负环即可判断是否最优. 沿负环增广一轮即可得到更优方案. 考虑到制作 ...
- poj2175费用流消圈算法
题意: 有n个建筑,每个建筑有ai个人,有m个避难所,每个避难所的容量是bi,ai到bi的费用是|x1-x2|+|y1-y2|+1,然后给你一个n*m的矩阵,表示当前方案,问当前避难方案是否 ...
- POJ - 2175 Evacuation Plan (最小费用流消圈)
题意:有N栋楼,每栋楼有\(val_i\)个人要避难,现在有M个避难所,每个避难所的容量为\(cap_i\),每个人从楼i到避难所j的话费是两者的曼哈顿距离.现在给出解决方案,问这个解决方案是否是花费 ...
- POJ 2157 Evacuation Plan [最小费用最大流][消圈算法]
---恢复内容开始--- 题意略. 这题在poj直接求最小费用会超时,但是题意也没说要求最优解. 根据线圈定理,如果一个跑完最费用流的残余网络中存在负权环,那么顺着这个负权环跑流量为1那么会得到更小的 ...
- POJ 2516 基础费用流
题意 有n个顾客,m个供应商,k种货物,给你顾客对于每种货物的要求个数,和供应商对于每种货物的现有量,以及供应每种货物的时候供应商和顾客之间的运输单价,问你满足所有顾客的前提下的最小运输费 ...
- POJ 2135 简单费用流
题意: 题意是一个人他要从牧场1走到牧场n然后在走回来,每条路径只走一次,问全程的最短路径是多少. 思路: 这个题目挺简单的吧,首先要保证每条边只能走一次,然后还要要求费用最 ...
- POJ 2175 Evacuation Plan 费用流 负圈定理
题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在 ...
- poj 3680 Intervals(费用流)
http://poj.org/problem?id=3680 巧妙的构图. 题目:给定N个区间(ai,bi)权值wi,求最大权和且每个点最多覆盖K次. 构图:将区间端点离散化,将第i个点连第i+1个点 ...
随机推荐
- Talented Chef(简单题,被我想的太复杂了,用复杂的方法当然会超时咯,由此可见,并非所有题都是想的越多越好)
Description As we all know, Coach Gao is a talented chef, because he is able to cook M dishes in the ...
- Android百度地图之显示地图
添加地图显示 一.在百度官网下载相关的SDK (网址:http://developer.baidu.com/map/sdkandev-download.htm) 解压下载好的BaiduMap_Andr ...
- oracle数据库连接无响应的解决
昨天中午时,查询到服务器的数据流水最晚记录是早上8点的,现场查看服务日志很奇怪,日志输出显示挂死在数据库连接这一步.多次调试无果,随后百度发现有资料显示oracle 10.2.1的版本有登录无响应的B ...
- linux chmod权限
Linux chmod 命令 chmod用于改变文件或目录的访问权限.用户用它控制文件或目录的访问权限.该命令有两种用法.一种是包含 字母和操作符表达式的文字设定法:另一种是包含数字的数字设定法. 1 ...
- vnc server配置、启动、重启与连接,图形管理linux系统
环境:RedHat Linux 5企业版.Xwindows:gnome (红帽默认安装的图形界面) 尽管我们可以使用SSH连接远程通过字符界面来操作Linux,但是对于更多熟悉图形人来说是很不方便的, ...
- csharp .net vb 复制图像
.NET Compact Framework 不支持 Image.Clone 方法,可是仍能够复制图像和图像的某些部分.以下的演示例子演示怎样运行以下操作: 定义一个方法以创建位图. 定义一个重载方法 ...
- osgi实战学习之路:8. Service-3之ServiceTracker
通过ServiceTracker能够对查找的Service进行扩展 以下的demo引入装饰器模式对Service进行日志的扩展 demo: Provider student-manage/Activa ...
- 关于JSP post请求乱码的问题
解决用户请求页面乱的问题 1. 修改apache下的config文件夹下的server.XML文件 <Connector connectionTimeout="20000" ...
- AsyncTask究竟需要多少个线程
最起码两个:主线程和工作线程; 可以参考:http://zhidao.baidu.com/link?url=ho4UEcEbaogRZUFHwig1neSKR25b2zT9iXyM36hEgWTmvJ ...
- DP之花店橱窗布置
题目:https://www.smartoj.com/p/1286 分析:花瓶是有序的,花也是有序的,这就保证了有序性,从而满足子解的全局最优,和无后效性.假设dp[i][j]表示前i 朵花,放在前j ...