2017乌鲁木齐网络赛 J题 Our Journey of Dalian Ends ( 最小费用最大流 )

Posted rubbishes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2017乌鲁木齐网络赛 J题 Our Journey of Dalian Ends ( 最小费用最大流 )相关的知识,希望对你有一定的参考价值。

题目链接

题意 : 给出一副图,大连是起点,终点是西安,要求你求出从起点到终点且经过中转点上海的最小花费是多少?

 

分析 :

最短路是最小费用最大流的一个特例,所以有些包含中转限制或者经过点次数有限制的最短路问题都可以考虑使用最小费用最大流来建图解决。

首先对于每个点都只能经过一次这个限制,在网络流中是比较常见的一个限制,只要将所有的点由一拆二且两点间连容量为 1 且花费为 0 的边。

这题的建图很巧妙,是将中转点作为汇点,提示到了这里不如停下来想想如何建图?

 

然后抽象出一个超级源点,然后将起点和终点与超级源点连一条容量为 1 且 花费为 0 的边,最后将上海这个中转点作为超级汇点。

最后跑出的最小费用最大流就是答案,当然最大流应当是要等于 2 的,如果没有解则说明 MaxFlow < 2。

 

技术分享图片
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
const int  INF = 0x3f3f3f3f;
struct st{ int from, to, w; }arr[10010];
map<string, int> mp;
int id;

struct Edge
{
    int from,to,cap,flow,cost;
    Edge(int u,int v,int ca,int f,int co):from(u),to(v),cap(ca),flow(f),cost(co){};
};

struct MCMF
{
    int n,m,s,t;
    vector<Edge> edges;
    vector<int> G[maxn];
    int inq[maxn];//是否在队列中
    int d[maxn];//距离
    int p[maxn];//上一条弧
    int a[maxn];//可改进量

    void init(int n)//初始化
    {
        this->n=n;
        for(int i=0;i<=n;i++)
            G[i].clear();
        edges.clear();
    }

    void AddEdge(int from,int to,int cap,int cost)//加边
    {
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        int m=edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool SPFA(int s,int t,int &flow,int &cost)//寻找最小费用的增广路,使用引用同时修改原flow,cost
    {
        for(int i=0;i<n;i++)
            d[i]=INF;
        memset(inq,0,sizeof(inq));
        d[s]=0;inq[s]=1;p[s]=0;a[s]=INF;
        queue<int> Q;
        Q.push(s);
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            inq[u]--;
            for(int i=0;i<G[u].size();i++)
            {
                Edge& e=edges[G[u][i]];
                if(e.cap>e.flow && d[e.to]>d[u]+e.cost)//满足可增广且可变短
                {
                    d[e.to]=d[u]+e.cost;
                    p[e.to]=G[u][i];
                    a[e.to]=min(a[u],e.cap-e.flow);
                    if(!inq[e.to])
                    {
                        inq[e.to]++;
                        Q.push(e.to);
                    }
                }
            }
        }
        if(d[t]==INF) return false;//汇点不可达则退出
        flow+=a[t];
        cost+=d[t]*a[t];
        int u=t;
        while(u!=s)//更新正向边和反向边
        {
            edges[p[u]].flow+=a[t];
            edges[p[u]^1].flow-=a[t];
            u=edges[p[u]].from;
        }
        return true;
    }

    int MincotMaxflow(int s,int t)
    {
        int flow=0,cost=0;
        while(SPFA(s,t,flow,cost));
        return cost;
    }
}MM;

inline void init()
{
    mp.clear();
    mp["Shanghai"] = 1;///中转点上海
    mp["Dalian"] = 2;///起点大连
    mp["Xian"] = 3;///终点西安
    id = 4;
}

int main(void)
{
    int nCase;
    cin>>nCase;
    while(nCase--){
        init();
        int M;
        cin>>M;
        string From, To;
        int Weight;
        for(int i=1; i<=M; i++){
            cin>>From>>To>>Weight;
            if(!mp.count(From)) mp[From] = id++;
            if(!mp.count(To)) mp[To] = id++;
            arr[i].from = mp[From];
            arr[i].to = mp[To];
            arr[i].w = Weight;
        }

        int n = id-1;
        MM.init(2*n+1);
        MM.AddEdge(0, 2, 1, 0);
        MM.AddEdge(0, 3, 1, 0);
        for(int i=1; i<=n; i++)
            MM.AddEdge(i, i+n, 1, 0);
        for(int i=1; i<=M; i++){
            MM.AddEdge(arr[i].from+n, arr[i].to, 1, arr[i].w);
            MM.AddEdge(arr[i].to+n, arr[i].from, 1, arr[i].w);
        }

        printf("%d\n", MM.MincotMaxflow(0, 1));
    }
    return 0;
}
View Code

 

以上是关于2017乌鲁木齐网络赛 J题 Our Journey of Dalian Ends ( 最小费用最大流 )的主要内容,如果未能解决你的问题,请参考以下文章

2017 新疆赛区网络赛 J Our Journey of Dalian Ends 费用流

C. Coconut(2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛)

2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛-A banana·

2017icpc 乌鲁木齐网络赛

2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛

2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 F. Islands