poj2135网络流费用流入门
题目部分
- 题意介绍:
主人公要从1号走到第N号点,再重N号点走回1号点,同时每条路只能走一次。
这是一个无向图。输入数据第一行是2个是N和M。N为点的数量,M为路径个数。
接下来M行是边的数据,每行输入3个数,边的两个端点a,b和边的长度v。
要你输出来回最短的路径长度。
题目确保存在来回的不重复路径
思路部分
这题可以转换成网络流的费用流。
来回并且路径不相同就相当于有用两条从1到N的路径。
把路径长度当成网络流里面每个流的费用,流量都设置成1这样就代表每条路径只能使用1次。增加2个点,源点和汇点,因为来回,就把源点到1建立一条流,流量为2(来回)费用为0,同样N到汇点建立一条流,流量为2费用为0。这样一个网络流就出来了。
这里费用流的增广路径是用spfa来找的,也就是找从源点到汇点流1个单位的流量最小的花费。这题找到路径后就进行正常的网络流增广路了,就是算费用的时候用+=N那个点的费用乘这个路径的最大流量
举个例子就比如坐车,要从a->g,途中要经过转车,每上一次车都要交钱,你一个人从a坐到g要花10块钱,然后你们组团一起坐车,问你一趟要花多少钱(一趟中人数是依据最小的车子能载几个人)。费用流的增广路就是这样,先找到这个路径,在看路径中的最大流量算出这条路径的费用。
用spfa不用dijkstra的原因是因为我们建立反向边的时候费用是正向边费用的负数,存在了负权值就不能用dijkstra了。
代码部分
这里输入一条边要建4条边,首先建a->b的有向边,要同时建立反向边,再建b->a的有向边,一样建立反向边。
这里node数组下标要从偶数开始用起,因为底下的增广路那块(也就是+-最小边那里)用到了 ^1 这个技巧,偶数与1异或就会加1,奇数与1异或就会减1。这样如果用正向的边,那么边的下标是偶数,对应的反向边下标就+1就可以找到,如果用的是反向边,那么边的下标是奇数,对应的正向边下标-1就可以找到。
我这里的Node结构体加了个s是这条边的起始点。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<fstream>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
fstream fin("1.txt");
//streambuf *buf = cin.rdbuf(fin.rdbuf());//用于重定项输入改成,把cin当成fin
const int inf = 1 << 29;
const int MAXN = 1010;
const int MAXM = 40010;
struct Node
{
int s;
int to;
int next;
int capacity;
int value;
};
int n, m;
int index;
Node node[MAXM];
int head[MAXN];
int pre[MAXN];
int dis[MAXN];
bool vis[MAXN];
void init()
{
index = 0;
memset(head, -1, sizeof(head));
memset(node, 0, sizeof(node));
}
void addedge(int a, int b, int v, int c)
{
node[index].to = b;
node[index].s = a;
node[index].value = v;
node[index].capacity = c;
node[index].next = head[a];
head[a] = index++;
node[index].to = a;
node[index].s = b;
node[index].value = -v;
node[index].capacity = 0;
node[index].next = head[b];
head[b] = index++;
}
bool spfa(int s, int t, int nnum)
{
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
for (int i = 0; i <= nnum; i++)
{
dis[i] = inf;
}
queue<int> que;
que.push(s);
dis[s] = 0;
vis[s] = true;
while (!que.empty())
{
int temp = que.front();
que.pop();
vis[temp] = false;
for (int i = head[temp]; i != -1; i = node[i].next)
{
if (node[i].capacity)
{
int ne = node[i].to;
if (dis[temp] + node[i].value < dis[ne])
{
dis[ne] = dis[temp] + node[i].value;
pre[ne] = i;
if (!vis[ne])
{
vis[ne] = true;
que.push(ne);
}
}
}
}
}
if (dis[t] == inf)
return false;
return true;
}
int getMincost(int s, int t, int nnum)
{
int ans_flow = 0;
int ans_cost = 0;
int temp, minc;
while (spfa(s, t, nnum))
{
temp = t;
minc = inf;
while (pre[temp] != -1)
{
minc = min(node[pre[temp]].capacity, minc);
temp = node[pre[temp]].s;
}
temp = t;
while (pre[temp] != -1)
{
node[pre[temp]].capacity -= minc;
int ss = pre[temp] ^ 1;
node[ss].capacity += minc;
temp = node[pre[temp]].s;
}
ans_cost += dis[t] * minc;
}
return ans_cost;
}
int main()
{
int a, b, v;
int s, t, result;
while (cin >> n >> m)
{
init();
for (int i = 0; i < m; i++)
{
cin >> a >> b >> v;
addedge(a, b, v, 1);
addedge(b, a, v, 1);
}
s = n + 1;
t = s + 1;
addedge(s, 1, 0, 2);
addedge(n, t, 0, 2);
result = getMincost(s, t, t);
cout << result << endl;
}
return 0;
}