最小费用最大流 ---- 2017icpc青岛现场赛 K Our Journey of Xian Ends (拆点控制原图点度 + 中间必经过的点设置成源点 + 起点设成汇点)

题目链接


题目大意:

题目贼恶心难读
就是你出发地是"Xian"西安,现在你要出发到上海然后再去青岛,然后回到上海

飞机场限制:就是每个机场只能降落和起飞一次

上海的限制:上海有两个机场,叫虹桥和浦东机场,你要是要是降落在浦东机场只能到虹桥出发去青岛并且你最后还要回到虹桥再回到浦东!!
如果是降落在虹桥就没有限制,你最后回到浦东机场就好了

给你所有的航线信息:问你最小花费是多少?


解题思路:

首先要最小花费那么肯定要最小费用流

对于每个飞机场只能起飞降落一次我们可以进行拆点进行限制:
每个点 u u u拆成 u 和 u ′ u和u' uu分别是入点和出点表示之间连接流量为1,费用为0的边

然后就是我们看看路线有多少种?

  1. 西安->虹桥->青岛->浦东
  2. 西安->浦东->虹桥->青岛->虹桥->浦东

那么就是两种路径
我们是不是把在原图建立双向边然后跑最大流就好了呢?源点连向西安,汇点连向上上海呢?
肯定不是!!
我们看看这样跑出什么结果!!

在这里插入图片描述
它会跑出直接从西安到浦东的结果的最大流那就不对了无法确定是否经过青岛?

我们观察一下合法路径长啥样?
在这里插入图片描述
想想我们之前的建图是不不保证一定经过青岛
那么我们可以这样给青岛连一个源点
那么整个图就会被切成两部分,然后我们再给浦东和虹桥连接汇点,那么最大流跑出来的结果就是上半部分的选的路径拼下半边的路径:就是青岛到虹桥的路径和浦东的路径+西安到这两个点的路径
本质上就是只考虑路径覆盖不考虑路径方向
在这里插入图片描述

通过观察上图
首先我们知道青岛出发要发出两条边那么源点向青岛发出的流量是2
西安只选一个方向走那么源点向西安的发的流量就是1

然后你观察上面两个汇点虹桥和浦东,虹桥无论无何都要两条边,那么它向汇点连流量为2的边,浦东最多连接1条边那么就是连接流量为1的边

其他的边就是把点拆成两个就行了!!建的图和原图是一样的


AC code

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+8;
const int INF=0x7f7f7f7f;
struct fuck{
    int u,v,cap,w,next;
}edge[maxn<<4];
int head[maxn<<1];
int tol;
void init()
{
    tol=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int w,int cap)
{
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol].cap=cap;
    edge[tol].w=w;
    edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].u=v;
    edge[tol].v=u;
    edge[tol].w=-w;
    edge[tol].cap=0;
    edge[tol].next=head[v];
    head[v]=tol++;
}
map<string,int> mp;
int idx;
int getid(char s[])
{
    if(mp.count(s)!=0)
        return mp[s];
    mp[s]=idx;addedge(idx*2-1,idx*2,0,1);idx++;
    return idx-1;
}
int dis[maxn<<1],pre[maxn<<1];
bool vis[maxn<<1];
bool spfa(int sour,int sink)
{
    queue<int>    q;
    q.push(sour);
    memset(dis,INF,sizeof(dis));
    memset(vis,false,sizeof(vis));
    dis[sour]=0;vis[sour]=true;pre[sour]=-1;
    int i,u,v;
    while(!q.empty())
    {
        u=q.front();q.pop();
        vis[u]=false;
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            v=edge[i].v;
            if(edge[i].cap>0&&edge[i].w+dis[u]<dis[v])
            {
                dis[v]=dis[u]+edge[i].w;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=true,q.push(v);
            }
        }
    }
    if(dis[sink]>=INF)   return false;
    return true;
}
void Minicost(int sour,int sink)
{
    int co,fl;
    co=fl=0;
    while(spfa(sour,sink))
    {
        int mi=INF;
        for(int i=pre[sink];i!=-1;i=pre[edge[i^1].v]){
            if(mi>edge[i].cap)
                mi=edge[i].cap;
        }
        for(int i=pre[sink];i!=-1;i=pre[edge[i^1].v]){
            edge[i].cap-=mi;
            edge[i^1].cap+=mi;
        }
        fl+=mi;
        co+=dis[sink]*mi;
    }
    if(fl==3)
        printf("%d\n",co);
    else
        printf("-1\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int m;
        scanf("%d",&m);
        init();idx=1;
        mp.clear();
        mp["Xian"]=idx;addedge(idx*2-1,idx*2,0,1);idx++;
        mp["Qingdao"]=idx;addedge(idx*2-1,idx*2,0,2);idx++;
        mp["Hongqiao"]=idx;addedge(idx*2-1,idx*2,0,2);idx++;
        mp["Pudong"]=idx;addedge(idx*2-1,idx*2,0,1);idx++;
        while(m--)
        {
            char s[12],c[12];
            int w;
            scanf("%s%s%d",s,c,&w);
            int u=getid(s);
            int v=getid(c);
            addedge(u*2,v*2-1,w,INF);
            addedge(v*2,u*2-1,w,INF);
        }
        int sour=0,sink=idx*2-1;
        addedge(sour,3*2-1,0,2);
        addedge(sour,4*2-1,0,1);
        addedge(1*2,sink,0,1);
        addedge(2*2,sink,0,2);
        Minicost(sour,sink);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值