POJ 2135 /// 最小费用流

题目大意:

给定一个n个点m条边的无向图

求从点1去点n再从点n回点1的不重叠(同一条边不能走两次)的最短路

挑战P239

求去和回的两条最短路很难保证不重叠

直接当做是由1去n的两条不重叠的最短路

这样就变成了由1去n流量为2的最小费用流

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N=1005;
int n,m;
struct EDGE { int v,w,c,r; };
vector <EDGE> E[N];
void addE(int u,int v,int w,int c) {
    E[u].push_back((EDGE){v,w,c,E[v].size()});
    E[v].push_back((EDGE){u,0,-c,E[u].size()-1});
}
int dis[N], pv[N] ,pe[N];
int minCFlow(int s,int t,int f) {
    int res=0;
    while(f>0) {
        /// Bellman-Ford求s到t最短路
        memset(dis,INF,sizeof(dis));
        memset(pv,0,sizeof(pv));
        dis[s]=0;
        bool upD=1;
        while(upD) {
            upD=0;
            for(int i=0;i<=n;i++) { // 通过i点
                if(dis[i]==INF) continue;
                for(int j=0;j<E[i].size();j++) { // 更新E[i][j]点的最短路
                    EDGE& e=E[i][j];
                    if(e.w>0 && dis[e.v]>dis[i]+e.c) { // 边容量>0才能走
                        dis[e.v]=dis[i]+e.c; // 找到更短的路 更新
                        pv[e.v]=i, pe[e.v]=j; // 记录前驱点及边 便于通过e.v找到i点
                        upD=1;
                    }
                }
            }
        }
        if(dis[t]==INF) return -1; // s不能到t 不能增广

        int d=f; // 找到本轮实际能够流出的流量(即实际用掉的容量)
        for(int i=t;pv[i];i=pv[i])
            d=min(d,E[pv[i]][pe[i]].w);
        f-=d; // 容量消耗
        res+=d*dis[t]; // 计算本轮花费
        for(int i=t;pv[i];i=pv[i]) {
            EDGE& e=E[pv[i]][pe[i]];
            e.w-=d;
            E[i][e.r].w+=d;
        } // 更新边的容量
    }
    return res;
}
int main()
{
    while(~scanf("%d%d",&n,&m)) {
        int s=1, t=n;
        for(int i=0;i<m;i++) {
            int u,v,c; scanf("%d%d%d",&u,&v,&c);
            addE(u,v,1,c); addE(v,u,1,c);
            // 建立u到v容量大小为1费用为c的边
        }
        printf("%d\n",minCFlow(s,t,2));
        // 求s到t传输大小为2(即最大容量为2)的最小费用流
    }

    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/zquzjx/p/10153650.html