题意:给定一个最小费用流的模型,根据给定的数据判定是否为最优解,如果不为最优解则给出一个比给定更优的解即可。不需要得出最优解。

解法:由给定的数据能够得出一个残图,且这个图满足了最大流的性质,判定一个残图是否满足最小费用最大流的判定依据类比于最大流中的层次图的构建,此时只需要判定图中是否存在负环即可。在写的过程中由于图中的编号和抽象了之后的编号有一个变换关系,所以处理要小心,一开始直接写了个最小费用最大流,通过费用来判定是否为最优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 最小费用流、负环判定的更多相关文章

  1. POJ - 2175 Evacuation Plan (最小费用流消圈)

    题意:有N栋楼,每栋楼有\(val_i\)个人要避难,现在有M个避难所,每个避难所的容量为\(cap_i\),每个人从楼i到避难所j的话费是两者的曼哈顿距离.现在给出解决方案,问这个解决方案是否是花费 ...

  2. poj 2175 Evacuation Plan 最小费用流判定,消圈算法

    题目链接 题意:一个城市有n座行政楼和m座避难所,现发生核战,要求将避难所中的人员全部安置到避难所中,每个人转移的费用为两座楼之间的曼哈顿距离+1,题目给了一种方案,问是否为最优方案,即是否全部的人员 ...

  3. POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)

    http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  4. POJ 2175 Evacuation Plan 费用流 负圈定理

    题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在 ...

  5. POJ 2175 Evacuation Plan

    Evacuation Plan Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. Origina ...

  6. POJ.2175.Evacuation Plan(消圈)

    POJ \(Description\) \(n\)个建筑物,每个建筑物里有\(a_i\)个人:\(m\)个避难所,每个避难所可以容纳\(b_i\)个人. 给出每个建筑物及避难所的坐标,任意两点间的距离 ...

  7. ACM: POJ 3259 Wormholes - SPFA负环判定

     POJ 3259 Wormholes Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu   ...

  8. poj 3621 二分+spfa判负环

    http://poj.org/problem?id=3621 求一个环的{点权和}除以{边权和},使得那个环在所有环中{点权和}除以{边权和}最大. 0/1整数划分问题 令在一个环里,点权为v[i], ...

  9. POJ 3259 Wormholes(SPFA判负环)

    题目链接:http://poj.org/problem?id=3259 题目大意是给你n个点,m条双向边,w条负权单向边.问你是否有负环(虫洞). 这个就是spfa判负环的模版题,中间的cnt数组就是 ...

随机推荐

  1. Linux下通过crontab及expect实现自动化处理

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 目标 为实现每天定时从其他服务器上复制文件到本地,需要使用crontab建立定时任务,并通过scp进行Linux之间的文件复制. ...

  2. jQuery添加删除元素

    $(document).ready(function () { $('#radioExtranet').on('click', function () { showProjectInformation ...

  3. 每日一九度之 题目1023:EXCEL排序

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:18804 解决:4240 题目描述:     Excel可以对一组纪录按任意指定列排序.现请你编写程序实现类似功能.     对每个测试用例 ...

  4. java 基本类型之间的转换

    基本数据类型从低级到高级是:byte  short int long float double ,char 类型比int 类型之后的都要低 下面通过一个例子说明: import javax.swing ...

  5. NTP客户端的设置

    LINUX做为客户端自动同步时间 如果想定时进行时间校准,可以使用crond服务来定时执行. 编辑 /etc/crontab 文件 加入下面一行:   30 8 * * * root /usr/sbi ...

  6. android Activity的启动模式

    Android中Activity启动模式详解   在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启 ...

  7. stl map底层之红黑树插入步骤详解与代码实现

    转载注明出处:http://blog.csdn.net/mxway/article/details/29216199 本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进 ...

  8. poj1961 Period

    我们考虑KMP算法中fail失配指针的意义. 对于一个模式串(Pattern),位置i对应的失配指针fail[i]是那个位置: 这个位置满足的条件是,子串[0, fail[i])是位置i(不含)的后缀 ...

  9. nginx安装后出现502 Bad Gateway 错误解决办法

    1. 打开php-fpm.conf 2.将lisen值修改为 listen = 127.0.0.1:9000 并保存 3.重启服务/etc/init.d/php-fpm restart

  10. c#代码画图

    说明:此示例代码在我做的一个项目中  不过还是可以学习一下 一:直角坐标系显示数据 先看效果图: