2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 J. Our Journey of Dalian Ends [网络流]

题意:给你n条边的图,求从Dalian到Xian经过Shanghai的最短路长度,每个点只能经过一次。

题解:由于只有10000条边,所以点数为100的时候是复杂度最大的图,所以我们考虑用网络流,拆点限制每个点只能走一次,Shanghai连源点,Dalain与Xian连汇点,跑费用流。

AC代码:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <complex>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cassert>
#include<string>
using namespace std;
typedef long long ll;   
#define maxn 200010
#define inf 0x3f3f3f3f
#define rep(i,s,t) for(ll i=s;i<=t;i++)
#define MM 200000
struct MCMF{
    struct Edge{
        ll from,to,cap,flow,cost,next;
        Edge(){}
        Edge(ll from,ll to,ll cap,ll flow,ll cost,ll next){
            this->from=from;
            this->to=to;
            this->cap=cap;
            this->flow=flow;
            this->cost=cost;
            this->next=next;
        }
    }ed[666666];
    ll n,m,s,t,head,tail,lnum;
    ll vis[MM],dis[MM],pre[MM];//pre 上一条弧
    ll cag[MM],que[MM*3],start[MM];//cag 可改进量

    void init(){
        lnum=0;
        rep(i,s,t) start[i]=-1;
    }

    void add(ll x,ll y,ll v,ll c){
        ed[lnum]=Edge(x,y,v,0,c,start[x]);
        start[x]=lnum++;
        ed[lnum]=Edge(y,x,0,0,-c,start[y]);
        start[y]=lnum++;
    }

    bool spfa(ll& flow,ll& cost){
        rep(i,s,t) dis[i]=inf,vis[i]=0;
        head=tail=0;
        dis[s]=0;vis[s]=1;pre[s]=0;cag[s]=inf;
        que[tail++]=s;
        while(head<tail){
            ll x=que[head++];
            vis[x]=0;
            for(ll i=start[x];~i;i=ed[i].next){
                Edge& e=ed[i];
                if(e.cap>e.flow&&dis[e.to]>dis[x]+e.cost){
                    dis[e.to]=dis[x]+e.cost;
                    pre[e.to]=i;
                    cag[e.to]=min(cag[x],e.cap-e.flow);
                    if(!vis[e.to]){
                        que[tail++]=e.to;
                        vis[e.to]=1;
                    }
                }
            }
        }
        if(dis[t]==inf) return false;
        flow+=cag[t];
        cost+=dis[t]*cag[t];
        ll x=t;
        while(x!=s){
            ed[pre[x]].flow+=cag[t];
            ed[pre[x]^1].flow-=cag[t];
            x=ed[pre[x]].from;
        }
        return true;
    }

    ll Mincost(){
        ll flow=0,cost=0;
        while((spfa(flow,cost)));
        if(flow!=2) return -1;
        return cost;
    }

}c1;
ll m;
ll n;
map<string,ll>mp;
string s1,s2;
int main(void){
	ll t;
	scanf("%lld",&t);
	while(t--){
		n=0;
		mp.clear();
		scanf("%lld",&m);

		c1.n=40000+2;
		c1.s=0;
		c1.t=40000+1;
		c1.init();
		for(ll i=0;i<m;i++){
			cin>>s1>>s2;
			ll d; scanf("%lld",&d);
			if(mp[s1]==0){
				mp[s1]=++n;
				if(s1=="Shanghai");
				
				else c1.add(mp[s1],mp[s1]+20000,1,0);
			}
			if(mp[s2]==0){
				mp[s2]=++n;
				if(s2=="Shanghai");
				
				else c1.add(mp[s2],mp[s2]+20000,1,0);
			}
			
			
				if(s1=="Shanghai")c1.add(mp[s1],mp[s2],1,d);
			else c1.add(mp[s1]+20000,mp[s2],1,d);
			if(s2=="Shanghai")c1.add(mp[s2],mp[s1],1,d);
			else c1.add(mp[s2]+20000,mp[s1],1,d);
		}
		c1.add(c1.s,mp["Shanghai"],2,0);
		c1.add(mp["Xian"]+20000,c1.t,1,0);
		c1.add(mp["Dalian"]+20000,c1.t,1,0);
		ll ans=c1.Mincost();
		
		printf("%lld\n",ans);
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值