• 【网络流24题】航空线路问题(费用流)


    【网络流24题】航空线路问题(费用流)

    题面

    Cogs数据有误,提供洛谷题面

    题解

    这题和原来做过的一道题周游加拿大是一模一样的
    所以,这题DP+记录方案应该也是可行的

    来考虑网络流的做法
    现在的来回,被看成是去两次
    所以流量被限定死了,为2
    因此要考虑费用流来求解。

    每个点只能经过一次
    很显然先拆点
    如果一个城市被访问了
    那么,他的两个点直接的流量是一定存在的
    为了记录下这个点被访问过
    所以,给定它一个费用1

    然后其他的连边和原来做的题目没有什么区别
    对于每一条航线,从(i')(j)连边,强制方向,流量为1,容量为INF

    现在跑一边最大费用最大流就行了
    说白点就是费用全部取负然后跑最小流

    输出方案比较诡异
    直接看代码。。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define MAX 5000
    #define MAXL 500000
    #define INF 1000000000
    inline int read()
    {
    	int x=0,t=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return x*t;
    }
    struct Line
    {
    	int v,next,w,fy;
    }e[MAXL];
    int h[MAX],cnt=2;
    inline void Add(int u,int v,int w,int fy)
    {
    	e[cnt]=(Line){v,h[u],w,fy};h[u]=cnt++;
    	e[cnt]=(Line){u,h[v],0,-fy};h[v]=cnt++;
    }
    int pe[MAX],pr[MAX],dis[MAX];
    bool vis[MAX];
    int S,T,Cost,n,m,Flow;
    bool SPFA()
    {
    	memset(dis,63,sizeof(dis));
    	queue<int> Q;
    	Q.push(S);dis[S]=0;
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].next)
    		{
    			int v=e[i].v;
    			if(e[i].w&&dis[v]>dis[u]+e[i].fy)
    			{
    				dis[v]=dis[u]+e[i].fy;
    				pe[v]=i;pr[v]=u;
    				if(!vis[v])vis[v]=true,Q.push(v);
    			}
    		}
    		vis[u]=false;
    	}
    	if(dis[T]>=INF)return false;
    	int flow=INF;
    	for(int i=T;i!=S;i=pr[i])flow=min(flow,e[pe[i]].w);
    	for(int i=T;i!=S;i=pr[i])e[pe[i]].w-=flow,e[pe[i]^1].w+=flow;
    	Cost-=flow*dis[T];
    	Flow+=flow;
    	return true;
    }
    string name[MAX];
    map<string,int> M;
    vector<int> ans;
    void output(int u)
    {
    	ans.push_back(u);
    	if(u==n)return;
    	for(int i=h[u+n];i;i=e[i].next)
    		if(!vis[i]&&e[i^1].w){vis[i]=true;output(e[i].v);return;}
    }
    int main()
    {
    	cin>>n>>m;
    	S=0;T=n+n+1;
    	for(int i=1;i<=n;++i){cin>>name[i];M[name[i]]=i;}
    	for(int i=1;i<=m;++i)
    	{
    		string s1,s2;
    		cin>>s1>>s2;
    		int u=M[s1],v=M[s2];
    		if(u>v)swap(u,v);
    		Add(u+n,v,INF,0);
    	}
    	Add(S,1,2,0);Add(1,1+n,2,0);
    	Add(n+n,T,2,0);Add(n,n+n,INF,0);
    	for(int i=2;i<n;++i)Add(i,i+n,1,-1);
    	while(SPFA());
    	if(Flow!=2){puts("No Solution!");return 0;}
    	printf("%d
    ",Cost+2);
    	bool fl=false;
    	output(1);
    	for(int i=0;i<ans.size();++i)cout<<name[ans[i]]<<endl;
    	ans.clear();output(1);
    	for(int i=ans.size()-1;i>=0;--i)if(ans[i]!=n)cout<<name[ans[i]]<<endl;
    	return 0;
    }
    
    
  • 相关阅读:
    《别让不会记笔记害了你》豆瓣:3.9
    《费曼学习法用输出倒逼输入》 豆瓣:6.6
    《面人麻生》豆瓣:8.8 &《哈瓦那》豆瓣:7.8
    C# 获取汉语拼音全码及简码
    SQL Server 获取汉语拼音简码
    ASP.NET 3.5中的ListView控件和DataPager控件
    Appliction_BeginRequest
    身份证验证
    取得数据库表信息的Sql语句
    Spark shuffle失败的可能原因 及解决办法
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8186159.html
Copyright © 2020-2023  润新知