2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 J.Our Journey of Dalian Ends【最小费用最大流】

Life is a journey, and the road we travel has twists and turns, which sometimes lead us to unexpected places and unexpected people.

Now our journey of Dalian ends. To be carefully considered are the following questions.

Next month in Xian, an essential lesson which we must be present had been scheduled.

But before the lesson, we need to attend a wedding in Shanghai.

We are not willing to pass through a city twice.

All available expressways between cities are known.

What we require is the shortest path, from Dalian to Xian, passing through Shanghai.

Here we go.

Input Format

There are several test cases.

The first line of input contains an integer tt which is the total number of test cases.

For each test case, the first line contains an integer m~(m\le 10000)m (m10000) which is the number of known expressways.

Each of the following mm lines describes an expressway which contains two string indicating the names of two cities and an integer indicating the length of the expressway.

The expressway connects two given cities and it is bidirectional.

Output Format

For eact test case, output the shortest path from Dalian to Xian, passing through Shanghai, or output -11 if it does not exist.

样例输入
3
2
Dalian Shanghai 3
Shanghai Xian 4
5
Dalian Shanghai 7
Shanghai Nanjing 1
Dalian Nanjing 3
Nanjing Xian 5
Shanghai Xian 8
3
Dalian Nanjing 6
Shanghai Nanjing 7
Nanjing Xian 8
样例输出
7
12
-1
题目来源

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

题目大意:


我们要从大连到西安,中途必须要先到上海一趟,现在要求每个地方只能去一次,问从大连到西安中途经过上海的最小路径花费。


思路:


①要求每个点只能去一次的话,我们不难想到网络流拆点。


②考虑到要求的是最小路径,那么肯定是最小费用流。我们中间必须经过上海,又因为是无向图,那么我们不妨设定上海为起点,大连和西安为终点,我们要从上海到这两个地方去。


③那么建图:

每个点和其拆点建立一条花费为0流为1的边。

每条原图边建立一条花费为w流为INF的边。

建立源点,连入上海这个点,花费为0.流为2。

建立汇点,将西安和大连连入汇点,花费为0,流为1.


然后跑最小费用最大流,如果流达到2,说明我们可以从上海到两个点,那么输出最小费用即可。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<map>
#include<iostream>
#include<queue>
using namespace std;
struct node
{
    int from;
    int to;
    int w;
    int f;
    int num;
    int next;
}e[2000000];
int head[150000];
int vis[150000];
int dis[150000];
int pre[150000];
int path[150000];
map<string,int>sss;
int Shanghai,Dalian,Xian;
int n,ss,tt,cont;
void add(int from,int to,int f,int w)
{
    e[cont].to=to;
    e[cont].f=f;
    e[cont].w=w;
    e[cont].num=cont;
    e[cont].next=head[from];
    head[from]=cont++;
}
int SPFA()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=tt;i++)dis[i]=0x3f3f3f3f;
    dis[ss]=0;
    queue<int >s;
    s.push(ss);
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            int f=e[i].f;
            if(f&&dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                pre[v]=u;
                path[v]=e[i].num;
                if(vis[v]==0)
                {
                    vis[v]=1;
                    s.push(v);
                }
            }
        }
    }
    if(dis[tt]!=0x3f3f3f3f)return 1;
    else return 0;
}
void Slove()
{
    int ans=0;
    int maxflow=0;
    while(SPFA()==1)
    {
        int minn=0x3f3f3f3f;
        for(int i=tt;i!=ss;i=pre[i])
        {
            minn=min(minn,e[path[i]].f);
        }
        for(int i=tt;i!=ss;i=pre[i])
        {
            e[path[i]].f-=minn;
            e[path[i]^1].f+=minn;
        }
        maxflow+=minn;
        ans+=dis[tt]*minn;
    }
    if(maxflow==2)
    {
        printf("%d\n",ans);
    }
    else printf("-1\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        sss.clear();
        cont=0;
        memset(head,-1,sizeof(head));
        n=0;
        int m;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            char name1[1500],name2[1600];
            int w;
            scanf("%s%s%d",name1,name2,&w);
            if(sss[name1]==0)
            {
                ++n;sss[name1]=n;++n;
            }
            if(sss[name2]==0)
            {
                ++n;sss[name2]=n;++n;
            }
            int u=sss[name1],v=sss[name2];
            add(u+1,v,0x3f3f3f3f,w);
            add(v,u+1,0,-w);
            add(v+1,u,0x3f3f3f3f,w);
            add(u,v+1,0,-w);
        }
        for(int i=1;i<=n;i+=2)add(i,i+1,1,0),add(i+1,i,0,0);
        if(sss["Dalian"]==0||sss["Shanghai"]==0||sss["Xian"]==0)
        {
            printf("-1\n");
        }
        else
        {
            Dalian=sss["Dalian"];
            Shanghai=sss["Shanghai"];
            Xian=sss["Xian"];
            ss=n+1,tt=ss+1;
            add(Shanghai,Shanghai+1,1,0);
            add(Shanghai,Shanghai+1,0,0);
            add(ss,Shanghai,2,0);
            add(Shanghai,ss,0,0);
            add(Dalian+1,tt,1,0);
            add(Xian+1,tt,1,0);
            Slove();
        }
    }
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值