洛谷 - P2770 航空路线问题(最大费用最大流+路径打印)

题目链接:点击查看

题目大意:给出一个由n个点及m条边组成的无向图,现在要求从点1出发,到达点n,再回到点1,一路上经过尽可能多的点,并且保证除了起点和终点外的每个点至多只能经过一次,并输出路径

题目分析:从点1出发到点n再回到点1,这个题目之前做过类似的,不过那个题目是要求最短路,用的是最小费用最大流,回到这个题目来看,要求尽可能多的经过点,也就说明每个点对答案的贡献为1,所以要用最大费用最大流,而权值在点上,很自然的想到需要拆点限流,所以建图方式就出来了:

  1. 源点->点1入点,流量为2,花费为0
  2. 点1入点->点1出点,流量为2,花费为1
  3. 点n入点->点n出点,流量为2,花费为1
  4. 其余点入点->其余点出点,流量为1,花费为1
  5. 某个点的出点->对应连边点的入点,流量为1,花费为0
  6. 点n出点->汇点,流量为2,花费为0

如此建图后,直接跑最大费用最大流就可以进一步判断了,现在可以得到了一个ans代表的最大花费,以及max_flow代表最大流,显然当最大流等于2的时候说明可以在原图中找到两条不想交的从起点到终点的路径,此时的花费为ans-2,因为起点和终点被计算了两次,所以减去2就好了,max_flow不等于2的时候直接输出No Solution!就好了,但是真的是这样吗?如果这样判断交上去有一个测试点过不去,这就是这个题目的一个小坑了,如果整张图中只有一条路径是从点1直接到点n的,这样的图虽然max_flow等于1,但是却是符合条件的特例,需要特判一下

现在只是判断出是否有解,最后还需要输出路径,其实直接跑dfs就好了,因为每条连边的流量都设置为1,所以如果这条边对答案做出了贡献,那么他的流量就会变成0,对于每个点寻找一下他的下一个符合要求的点,一路寻找,从起点找到终点就好了,记得用一个vis数组标记一下哪些点已经使用过了,因为对于每个点至多只能使用一次嘛,第一遍dfs正序输出,第二遍dfs倒序输出,写法类似于树的前序遍历和后序遍历,剩下的对于细节仔细处理一下就好了

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;

const int N=1e5+100;//点
 
const int M=1e5+100;//边

unordered_mapmp;

string str[N];
 
struct Edge
{
	int to,w,cost,next;
}edge[M];
 
int head[N],cnt,max_flow,n,m,st=N-1,ed=st-1;
 
void addedge(int u,int v,int w,int cost)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].cost=cost;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].w=0;
	edge[cnt].cost=-cost;
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
 
int d[N],incf[N],pre[N];
 
bool vis[N];
 
bool spfa(int s,int t)
{
	memset(d,0xcf,sizeof(d));
	memset(vis,false,sizeof(vis));
    memset(pre,-1,sizeof(pre));
	queueq;
	q.push(s);
	vis[s]=true;
	incf[s]=inf;
	d[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			int cost=edge[i].cost;
			if(!w)
				continue;
			if(d[v]x-n)
		{
			dfs1(to+n);
			break;
		}
	}
}

void dfs2(int x)
{
	vis[x-n]=true;
	for(int i=head[x];i!=-1;i=edge[i].next)
	{
		int to=edge[i].to;
		int w=edge[i].w;
		if(w==0&&to!=st&&to!=ed&&to<=n&&to>x-n&&!vis[to])
		{
			dfs2(to+n);
			break;
		}
	}
	cout<>s;
		mp[s]=i;
		str[i]=s;
		addedge(i,i+n,1,1);//拆点限流
		if(i==1||i==n)
			addedge(i,i+n,1,1); 
	}
	addedge(st,1,2,0);
	addedge(n+n,ed,2,0);
	bool flag=false;
	while(m--)
	{
		string a,b;
		cin>>a>>b;
		if(mp[a]>mp[b])
			swap(a,b);
		addedge(mp[a]+n,mp[b],1,0);
		if(mp[a]==1&&mp[b]==n)
			flag=true;
	}
	int ans=solve(st,ed);
	if(max_flow==2)
	{
		printf("%d\n",ans-2);
		dfs1(1+n);
		dfs2(1+n);
	}
	else if(max_flow==1&&flag)
	{
		puts("2");
		cout<

 

你可能感兴趣的:(图论,网络流24题)