POJ-2175 Evacuation Plan 最小费用流、负环判定
题意:给定一个最小费用流的模型,根据给定的数据判定是否为最优解,如果不为最优解则给出一个比给定更优的解即可。不需要得出最优解。
解法:由给定的数据能够得出一个残图,且这个图满足了最大流的性质,判定一个残图是否满足最小费用最大流的判定依据类比于最大流中的层次图的构建,此时只需要判定图中是否存在负环即可。在写的过程中由于图中的编号和抽象了之后的编号有一个变换关系,所以处理要小心,一开始直接写了个最小费用最大流,通过费用来判定是否为最优TLE。找负环的时候,该题宜从汇点开始遍历,因为源点发出的边肯定全部满流(由于要疏散所有的人),而市政大楼与避难所在内部又分别没有边相连,所以要形成回路就必定会经过汇点。spfa跳出来的点也不一定就是环上的点,需要自己找出环上的一个点,方法就是沿着一条路进行标记,如果标记到了已经标记的点,那么这个点就一定在环上。
具体残图构图:
市政大楼(b)到避难所(s)两两之间一定有边,这在原图中也是不改变的,费用为两点之间的曼哈顿距离加1;
如果b到s已经有流量,那么s到b也一定有负费用的流量,费用为两点之间的曼哈顿距离加1;
如果源点到b有流量,那么b到源点一定有负费用的流量,如果从源点发出的边没有满流,那么仍然有边到达b,费用为0;
如果s到汇点有流量,那么汇点到s一定有负费用的流量,如果汇集到汇点的边没有满流,那么仍然有边到达汇点,费用为0;
代码如下:
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std; const int INF = 0x3f3f3f3f;
int N, M, SS, TT;
int dis[], vis[], cnt[];
int mp[][];
int way[][];
int path[];
struct Point {
int x, y, c;
}b[], s[]; bool gao() { // 用来判定负圈的存在
int flag = false, pos;
memset(dis, 0x3f, sizeof (dis));
memset(vis, , sizeof (vis));
memset(cnt, , sizeof (cnt));
queue<int>q;
dis[TT] = , vis[TT] = , cnt[TT] = ;
q.push(TT);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = ;
for (int v = ; v <= TT; ++v) {
if (mp[u][v] != INF && dis[v] > dis[u] + mp[u][v]) {
dis[v] = dis[u] + mp[u][v];
path[v] = u;
if (!vis[v]) {
vis[v] = , ++cnt[v];
if (cnt[v] > TT) {
flag = true;
pos = v;
break;
}
q.push(v);
}
}
}
}
if (flag) {
puts("SUBOPTIMAL");
memset(vis, , sizeof (vis));
int u = path[pos], v;
while () {
if (!vis[u]) {
vis[u] = ;
u = path[u];
} else break;
}
int rec = u;
v = u, u = path[u];
while () {
if (u <= N) {
way[u][v-N]++;
} else {
way[v][u-N]--;
}
if (u == rec) break;
v = u, u = path[u];
}
for (int i = ; i <= N; ++i) {
for (int j = ; j <= M; ++j) {
printf(j == ? "%d" : " %d", way[i][j]);
}
puts("");
}
} else {
puts("OPTIMAL");
}
} int dist(int m, int n) {
return abs(b[m].x - s[n].x) + abs(b[m].y - s[n].y) + ;
} int num1[], num2[]; int main() {
while (scanf("%d %d", &N, &M) != EOF) {
SS = , TT = N+M+;
memset(num1, , sizeof (num1));
memset(num2, , sizeof (num2));
memset(mp, 0x3f, sizeof (mp)); // 若j>N, mp[i][j]表示从i到j-N的距离
for (int i = ; i <= N; ++i) {
scanf("%d %d %d", &b[i].x, &b[i].y, &b[i].c);
}
for (int i = ; i <= M; ++i) {
scanf("%d %d %d", &s[i].x, &s[i].y, &s[i].c);
}
for (int i = ; i <= N; ++i) {
for (int j = ; j <= M; ++j) {
scanf("%d", &way[i][j]);
mp[i][N+j] = dist(i, j);
num1[i] += way[i][j];
num2[j] += way[i][j];
if (way[i][j]) {
mp[N+j][i] = -dist(i, j);
}
}
}
for (int i = ; i <= N; ++i) {
if (num1[i] < b[i].c) mp[SS][i] = ;
if (num1[i]) mp[i][SS] = ;
}
for (int i = ; i <= M; ++i) {
if (num2[i] < s[i].c) mp[N+i][TT] = ;
if (num2[i]) mp[TT][N+i] = ;
}
gao();
}
return ;
}
TLE最小费用流代码,构边的方式是拆点,其实直接从源汇来控制流量也可。
/*
POJ-2175
题意:N个点,每个点有一定人数,现在要从N个点到M个点进行人员派送,判定给出的方案是否合理
否则输出相比较而言较优的一种 分析:将N个点与M个之间进行构边,拆点限制N+M个点的容量,然后构图,最小费用流
*/
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std; int N, M, LIM;
const int INF = 0x3f3f3f3f;
int ini;
int xi[], yi[], ci[];
int cp[][]; // 用来存储容量
int ct[][]; // 用来存储费用
int mp[][];
int path[];
int SS = , TT = ; // 固定好源点和汇点 int dist(int a, int b) {
return abs(xi[a] - xi[b]) + abs(yi[a] - yi[b]) + ;
} void build() {
LIM = N+M;
memset(cp, , sizeof (cp));
for (int i = ; i < LIM; ++i) { // 拆点
cp[i<<][i<<|] = ci[i];
ct[i<<][i<<|] = , ct[i<<|][i<<] = ;
}
for (int i = ; i < N; ++i) { // 从源点到市政大楼流量无穷大,费用为0,流量限制在拆点时确定
cp[SS][i<<] = INF;
ct[SS][i<<] = , ct[i<<][SS] = ;
}
for (int i = N; i < LIM; ++i) { // 从避难所到汇点的流量无穷大,费用为0
cp[i<<|][TT] = INF;
ct[i<<|][TT] = , ct[TT][i<<|] = ;
}
for (int i = ; i < N; ++i) { // 对从市政大楼到避难所构边
for (int j = N; j < LIM; ++j) {
cp[i<<|][j<<] = INF;
ct[i<<|][j<<] = dist(i, j);
ct[j<<][i<<|] = -dist(i, j);
}
}
} bool spfa() {
int dis[];
char vis[];
queue<int>q;
memset(dis, 0x3f, sizeof (dis));
memset(vis, , sizeof (vis));
path[SS] = -;
dis[SS] = ;
vis[SS] = ;
q.push(SS);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = ;
// printf("u = %d\n", u);
// getchar();
for (int v = ; v <= TT; ++v) {
if (cp[u][v] > && dis[u] + ct[u][v] < dis[v]) {
dis[v] = dis[u] + ct[u][v];
path[v] = u; // 记录最短路径
if (!vis[v] && v != TT) {
vis[v] = ;
q.push(v);
}
}
}
}
// printf("dis[TT] = %d\n", dis[TT]);
return dis[TT] != INF;
} void maxflow() {
// 最小费用最大流的主要思想就是寻找最短路,然后根据这条最短路进行增广
LIM = N+M;
int cost = ;
while (spfa()) {
int u = path[TT], v = TT, f = INF;
while (u != -) {
f = min(f, cp[u][v]);
// printf("cp[][] = %d, f = %d\n", cp[u][v], f);
v = u, u = path[u];
// printf("u = %d\n", u);
// getchar();
}
// printf("f = %d\n", f);
u = path[TT], v = TT;
while (u != -) {
cp[u][v] -= f, cp[v][u] += f;
cost += f * ct[u][v];
v = u, u = path[u];
}
}
// printf("cost = %d, ini = %d\n", cost, ini);
if (cost == ini) {
puts("OPTIMAL");
} else {
puts("SUBOPTIMAL");
for (int i = ; i < N; ++i) {
for (int j = N; j < LIM; ++j) {
printf(j == N ? "%d" : " %d", cp[j<<][i<<|]);
}
puts("");
}
}
} int main() {
// freopen("1.in", "r", stdin);
while (scanf("%d %d", &N, &M) != EOF) {
ini = ;
for (int i = ; i < N; ++i) {
scanf("%d %d %d", &xi[i], &yi[i], &ci[i]);
}
for (int i = ; i < M; ++i) {
scanf("%d %d %d", &xi[N+i], &yi[N+i], &ci[N+i]);
} // 读取每个点的左边和容量
for (int i = ; i < N; ++i) {
for (int j = ; j < M; ++j) {
scanf("%d", &mp[i][j]);
ini += mp[i][j] * dist(i, N+j);
}
}
build();
maxflow();
}
return ;
}
POJ-2175 Evacuation Plan 最小费用流、负环判定的更多相关文章
- POJ - 2175 Evacuation Plan (最小费用流消圈)
题意:有N栋楼,每栋楼有\(val_i\)个人要避难,现在有M个避难所,每个避难所的容量为\(cap_i\),每个人从楼i到避难所j的话费是两者的曼哈顿距离.现在给出解决方案,问这个解决方案是否是花费 ...
- poj 2175 Evacuation Plan 最小费用流判定,消圈算法
题目链接 题意:一个城市有n座行政楼和m座避难所,现发生核战,要求将避难所中的人员全部安置到避难所中,每个人转移的费用为两座楼之间的曼哈顿距离+1,题目给了一种方案,问是否为最优方案,即是否全部的人员 ...
- POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)
http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS Memory Limit: 65536K Total Submi ...
- POJ 2175 Evacuation Plan 费用流 负圈定理
题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在 ...
- POJ 2175 Evacuation Plan
Evacuation Plan Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Origina ...
- POJ.2175.Evacuation Plan(消圈)
POJ \(Description\) \(n\)个建筑物,每个建筑物里有\(a_i\)个人:\(m\)个避难所,每个避难所可以容纳\(b_i\)个人. 给出每个建筑物及避难所的坐标,任意两点间的距离 ...
- ACM: POJ 3259 Wormholes - SPFA负环判定
POJ 3259 Wormholes Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu ...
- poj 3621 二分+spfa判负环
http://poj.org/problem?id=3621 求一个环的{点权和}除以{边权和},使得那个环在所有环中{点权和}除以{边权和}最大. 0/1整数划分问题 令在一个环里,点权为v[i], ...
- POJ 3259 Wormholes(SPFA判负环)
题目链接:http://poj.org/problem?id=3259 题目大意是给你n个点,m条双向边,w条负权单向边.问你是否有负环(虫洞). 这个就是spfa判负环的模版题,中间的cnt数组就是 ...
随机推荐
- Android剪切板传递数据传递序列化对象数据
一.剪切板的使用介绍 1. 剪切板对象的创建 使用剪切板会用到,ClipboardManager对象,这个对像的创建不可以使用构造方法,主要是由于没有提供public的构造函数(单例模式),需要使用A ...
- eclipse 利用已有c++代码建工程,并编译执行
如果你想建一个带Makefile的c++ 工程 1. 新建一个C++空工程,工程类型是makefile project,选择Linux GCC: 2. 将源码连同makefile文件一同作为一个文件系 ...
- maven打包异常:软件包com.sun.org.apache.xml.internal.security.utils.Base64 不存在
maven打包异常:软件包com.sun.org.apache.xml.internal.security.utils.Base64 不存在 将jre/lib/rt.jar添加到maven的compi ...
- 怎么使用 Laravel 的服务容器来优化读写数据库中的 options关键词
其中我们可以最方便地利用的一个特性就是 Laravel 的服务容器了.在这里我不多赘述 Service Container 是个啥,想了解的可以自行搜索.不想了解的就只要大致知道它是个可以 绑定/取出 ...
- 收集 关于php的博文
1. 小狼的世界: 浅谈用php实现mvc:http://www.cnblogs.com/cocowool/archive/2009/09/08/1562874.html 关于MVC的定义和解释,可以 ...
- [HTML]网页开发学习笔记
为了要开发一套教学使用的教师管理系统,(客户需求使用网页做教师控制端口)我便学习了一下HTML网页开发. 很不错的学习开发的网站:http://www.w3school.com.cn/index.ht ...
- quick-cocos2d-x 接入支付宝(android)(转,待验证)
quick-cocos2d-x 实现在lua里面完成android支付宝的接入 一.支付宝注册是很麻烦的一个过程,本文就不解释了,想了解的去官网看下注册流程.然后下载他们的sdk-WS_SECURE_ ...
- Coco2dx 3D例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // add "HelloWorld" splash screen" ...
- Linux hrtimer分析(一)
http://blog.csdn.net/angle_birds/article/details/17375883 本文分析了Linux2.6.29中hrtimer的实现. Linux2.6中实现了一 ...
- htmlnav
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...