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

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

    本人昵称sky,欢迎与各位多多交流学习 这样的c程序想必大家都不陌生,fflush()这个函数有清除输入输出缓存的功能,那很多人就会问了,什么是清除输入输出缓存呢? 其实就是我们在printf输出的时 ...

  2. db2常用函数(1)

    VALUE函数 语法:VALUE(EXPRESSION1,EXPRESSION2) VALUE函数是用返回一个非空的值,当其第一个参数非空,直接返回该参数的值,如果第一个参数为空,则返回第一个参数的值 ...

  3. PANIC : Error configuring AvalonLogSystem :

    ll /tpsys/weblogic/user_projects/domains/mall/velocity.log 有1.7版本的的,不用这个1.4的.1.4会有这个问题: PANIC : Erro ...

  4. 20145227 《Java程序设计》实验四实验报告

    20145227 <Java程序设计>实验四实验报告 实验内容 安装Andriod Studio并配置软件 使用Andriod Studio软件实现Hello World的小程序 实验步骤 ...

  5. Maven invalid task...

    执行maven构建项目报错: Invalid task '‐DgroupId=*': you must specify a valid lifecycle phase, or a goal in th ...

  6. java中+的使用

    在java中+可以做为连接符和运算符两种使用方法 例如: 代码: int X=100; int Y=200; System.out.println("X+Y="  +  (X +  ...

  7. 开源数据库连接池proxool

    可以根据自己的实际情况,选择不同的配置,可以选择properties.XML.Servlet等配置,官方网站上都有介绍.   1.下载proxool 地址:http://proxool.sourcef ...

  8. Check the difficulty of problems

    Check the difficulty of problems Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 5830 Acc ...

  9. getopts

    http://blog.sina.com.cn/s/blog_81c2cf020100v0wh.html http://www.cnblogs.com/xiangzi888/archive/2012/ ...

  10. PowerShell调用jira rest api实现jira统计自动化

    通过调用JIRA Rest web api实现统计自动化,首先进行登录模拟: $content = @{username='用户名';password='密码'} $JSON=$content|con ...