poj2135网络流费用入门

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值