• ACM-ICPC 2017 Asia Qingdao Our Journey of Xian Ends


    链接:
    https://vjudge.net/problem/计蒜客-A1429
    这个题的题意很长,概括一下要点:
    有若干个机场,它们的名字仅有英文字母组成,且不超过10位。这些机场一定包含"Xian","Qingdao","Pudong","Hongqiao"。其中"Pudong"和"Hongqiao"之间有一条双向的免费铁路,你可以在这两个机场之间任意穿梭。有若干双向航线,双向飞行的费用一致,你只能沿着航线飞或者坐火车。
    你从"Xian"出发,到达"Pudong"或"Hongqiao"(这个或是逻辑or)。然后再飞到"Qingdao",之后到"Pudong",最后坐飞机出国,问直到出国前最少花费多少?
    注意务必按照顺序旅行。
    你不能在一个机场起飞超过1次,也不能在一个机场降落超过1次,但这不意味着你不能经过一个机场2次。
    你如果在"Pudong","Hongqiao"以外的机场,你还没有到达最终目的地"Pudong",你也不能坐火车,所以只能继续起飞。而如果你又回到了这个机场,你还需要再起飞,这样就超过1次起飞次数了。所以"Pudong","Hongqiao"以外的机场只能经过一次。
    "Pudong","Hongqiao"比较特殊,它们中间有免费铁路相连,而且"Pudong"是不允许在出国前起飞的,因为你还要从这里起飞出国。
    显然,在"Pudong"和"Hongqiao"之间反复横跳没有意义,我们不会连续的在两个机场间折返。
    你可以从"Xian"出发,到达"Hongqiao",然后飞到"Qingdao",最后飞到"Pudong"。
    你可以从"Xian"出发,到达"Hongqiao",然后坐火车到"Pudong",然后飞"Qingdao",但是你不能在"Pudong"起飞,所以这个路线有问题。
    你可以从"Xian"出发,到达"Pudong",然后飞"Qingdao",但是你不能在"Pudong"起飞,所以这个路线有问题。。
    你可以从"Xian"出发,到达"Pudong",然后坐火车去"Hongqiao",然后飞到"Qingdao",之后飞到"Hongqiao",然后坐火车去"Pudong"。

    所以综上,只有2条路线:
    Xian-Hongqiao-Qingdao-Pudong
    Xian-Pudong-Hongqiao-Qingdao-Hongqiao-Pudong

    这个题不能用最短路做,因为最短路很难限制你路径上点的顺序。

    这路要介绍一个思想:
    假如题目给定了一个无向图,要并求你从A出发,经过B,到达C,且每个点只能经过一次。你可以把这个A-B-C的路径拆分,认为是A-B,B-C两条路径。又因为是无向图,所以又可以认为是A-B,C-B两条路径。你可以从源点向A,C引1单位流量,从B点向汇点引出2单位流量,并且把每个点的流量(除了端点A,B,C)限制为1,不设边容量的限制,边的费用设置为边权,跑一个最小费用最大流即可。假如最终流量是2,那么就有解,解就是费用,否则无解。流可以直观的想象成路径,如果最终流量是2,那么从A-B,C-B必然存在不相交的路径。
    但是如果题目给定了一个无向图,要并求你从A出发,经过B,再经过C,最终到达D,且每个点只能经过一次,这就比较难搞了。
    如果还用路径拆分的办法,把A-B-C-D分成A-B,C-B,C-D,然后从源点向A引1单位流量,从源点向C引2单位流量,从B点向汇点引出2单位流量,从D点向汇点引出1单位流量。这样,如果总流量是3,有可能是A-B,C-B,C-D这样的3条路径,也有可能是A-D,C-B,C-B这样的3条路径,那很显然你不好区分这两种情况。
    但是看回题目,你的路线是Xian-Hongqiao-Qingdao-Pudong或Xian-Pudong-Hongqiao-Qingdao-Hongqiao-Pudong,计费的路径简写为A-B-C-D或A-D,B-C,C-B
    这意味着你不需要区分这两种路径的区别。

    除了上面的4个机场,每个机场拆成ar(到达)和de(起飞)两个点,从ar->de连一条容量为1费用为0的边。航线x-y需要从x的de到y的ar连边,从y的de到x的ar离岸边,费用都是边权。从源点向"Xian"连一条容量为1的边,从源点向"Qingdao"连一条容量为2的边,从"Hongqiao"向汇点连一条容量为2的边,从"Pudong"向汇点连一条容量为1的边。跑最小费用最大流,若流量为3,则费用就是解,反之无解。

    在输入时,给的都是字符串,你可以用string存储,并用map<string,int>给字符串编号,这样方便连边。建议把"Xian","Hongqiao","Qingdao","Pudong"先映射为1,2,3,4,方便处理。不用map映射也可以排序再二分查找或者建trie树查找,但是比较麻烦。

    由于边数m<=10000,流量是3,考虑一般出题人不会刻意卡spfa,跑一次最小费用最大流的时间开销不会超过1e6这个数量级,一共有160组样例,4s应该能跑过。
    代码如下:

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<map>
    using namespace std;
    const int MAXN=21000,MAXM=61000,inf=1000000000;
    struct edge
    {
    	int y,next,rev,c,cost;
    }graph[MAXM];
    struct pii
    {
    	int cost,flow;
    };
    pii mp(int cost,int flow)
    {
    	pii re;
    	re.cost=cost;
    	re.flow=flow;
    	return re;
    }
    int head[MAXN];
    int add(int num,int x,int y,int c,int cost)
    {
    //	printf("%d %d %d %d
    ",x,y,c,cost);
    	int e=num*2-1,rev=num*2;
    	graph[e].y=y;
    	graph[e].c=c;
    	graph[e].cost=cost;
    	graph[e].rev=rev;
    	graph[e].next=head[x];
    	head[x]=e;
    	
    	graph[rev].y=x;
    	graph[rev].c=0;
    	graph[rev].cost=-cost;
    	graph[rev].rev=e;
    	graph[rev].next=head[y];
    	head[y]=rev;
    }
    int pre_v[MAXN],pre_e[MAXN];
    int dis[MAXN],cnt_qu[MAXN];
    bool in_qu[MAXN];
    bool cmp(int x,int y,bool minn)//minn==1,<;minn==0,>
    {
    	if(minn)return x<y;
    	return x>y;
    }
    queue<int> q;
    void push(int x)
    {
    	cnt_qu[x]++;
    	in_qu[x]=1;
    	q.push(x);
    }
    int pop()
    {
    	int x=q.front();
    	in_qu[x]=0;
    	q.pop();
    	return x;
    }
    bool spfa(int s,int t,int all,bool minn)
    {
    	int init=inf;
    	if(minn==0)init=-inf;
    	
    	for(int i=1;i<=all;i++)dis[i]=init;
    	for(int i=1;i<=all;i++)in_qu[i]=0;
    	for(int i=1;i<=all;i++)cnt_qu[i]=0;
    	while(!q.empty())q.pop();
    	
    	dis[s]=0;
    	push(s);
    	while(!q.empty())
    	{
    		int now=pop();
    		//printf("%d %d %d
    ",now,dis[now],head[now]);
    		for(int k=head[now];k!=-1;k=graph[k].next)
    		{
    			int y=graph[k].y;
    			//printf("%d ",y);
    			if(graph[k].c>0)
    			{
    				if(cmp(dis[now]+graph[k].cost,dis[y],minn))
    				{
    					dis[y]=dis[now]+graph[k].cost;
    					pre_e[y]=k;
    					pre_v[y]=now;
    					if(!in_qu[y])push(y);
    					if(cnt_qu[y]>=all)
    					{
    						return 0;
    					}
    				}
    			}
    		}
    	}
    	return dis[t]!=init;
    }
    pii min_cost_flow(int s,int t,int all,bool minn)
    {
    	int cost=0,flow=0;
    	while(spfa(s,t,all,minn))
    	{
    		int now=t;
    		int f=inf;
    		while(now!=s)
    		{
    			int e=pre_e[now];
    			f=min(f,graph[e].c);
    			now=pre_v[now];
    		}
    		flow+=f;
    		now=t;
    		while(now!=s)
    		{
    			int e=pre_e[now];
    			int rev=graph[e].rev;
    			graph[e].c-=f;
    			graph[rev].c+=f;
    			now=pre_v[now];
    		}
    		cost+=f*dis[t];
    	}
    	return mp(cost,flow);
    }
    map<string,int> name;
    int de(int x)
    {
    	return x*2;
    }
    int ar(int x)
    {
    	return x*2-1;
    }
    int cnt_e=0;
    int solve(int n)
    {
    	int S=2*n+1,T=2*n+2;
    	for(int i=5;i<=n;i++)
    	{
    		add(++cnt_e,ar(i),de(i),1,0);
    	}
    	add(++cnt_e,ar(1),de(1),2,0);
    	add(++cnt_e,S,ar(1),2,0);
    	
    	add(++cnt_e,ar(2),de(2),1,0);
    	add(++cnt_e,S,ar(2),1,0);
    	
    	add(++cnt_e,ar(3),de(3),2,0);
    	add(++cnt_e,de(3),T,2,0);
    	
    	add(++cnt_e,ar(4),de(4),1,0);
    	add(++cnt_e,de(4),T,1,0);
    	
    	pii ans=min_cost_flow(S,T,T,1);
    	if(ans.flow<3)return -1;
    	return ans.cost;
    }
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--)
    	{
    		int num=0;
    		cnt_e=0;
    		name.clear();
    		int n;
    		scanf("%d",&n);
    		memset(head,-1,sizeof(head));
    		name["Hongqiao"]=1;
    		name["Pudong"]=2;
    		name["Qingdao"]=3;
    		name["Xian"]=4;
    		num=4;
    		for(int i=1;i<=n;i++)
    		{
    			string str1,str2;
    			int cost;
    			cin>>str1>>str2>>cost;
    			if(name[str1]==0)name[str1]=++num;
    			if(name[str2]==0)name[str2]=++num;
    			add(++cnt_e,de(name[str1]),ar(name[str2]),inf,cost);
    			add(++cnt_e,de(name[str2]),ar(name[str1]),inf,cost);
    		}
    		int ans=solve(num);
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    构造函数与其他类方法的区别
    将博客搬至51CTO
    易错点
    c++类构造函数详解
    将博客搬至CSDN
    实数,有理数,无理数,自然数,整数的概念分别是什么?
    code for qint function
    spline和Pchips的不同(matlab)
    Pseudocode MD5 CODE
    git基础介绍
  • 原文地址:https://www.cnblogs.com/ssdfzhyf/p/14474155.html
Copyright © 2020-2023  润新知