洛谷 P2770:航空路线问题(最大费用最大流 + 输出路径)

在这里插入图片描述
在这里插入图片描述

题解:分析一下,要求往返经过最多点的路径,可以转化为求两条从s到t的不相交的尽量长的路径,根据要求限制,无向边变成有向边。求两条不相交的路径,将每个点拆开(x,x’),可以用网络流。又要尽量长,可以用最大费用最大流。

有解无解判断一下是否最大流是否等于2。输出路径需要遍历所有的边流完的边。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int n,m;
map<string,int> mp;
map<int,string> np;
char tmp[200][200],s1[200],s2[200];
vector<int> g[maxn];
vector<int> a[3];
struct ss{
	int u,v,c,w,nxt;
}edg[maxn];
int head[maxn],cnt,d[maxn],vis[maxn],pre[maxn];
void init() {
	cnt = 0;
	memset(head,-1,sizeof head);
}
void add(int u,int v,int c,int w) {
	edg[cnt].u = u;
	edg[cnt].v = v;
	edg[cnt].c = c;
	edg[cnt].w = w;
	edg[cnt].nxt = head[u];
	head[u] = cnt++;
}
bool spfa(int s,int t) {
	queue<int> q;
	memset(vis,0,sizeof vis);
	memset(d,-inf,sizeof d);
	memset(pre,-1,sizeof pre);
	q.push(s);
	d[s] = 0;vis[s] = 1;
	while(!q.empty()) {
		int top = q.front();
		q.pop();
		vis[top] = 0;
		for(int i = head[top]; i + 1; i = edg[i].nxt) {
			int v = edg[i].v,c = edg[i].c,w = edg[i].w;
			if(c && d[v] < d[top] + w) {
				d[v] = d[top] + w;pre[v] = i;
				if(!vis[v]) {
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
	return pre[t] != -1;
}
int maxflow(int s,int t,int &cost) {
	int ans = 0;
	cost = 0;
	spfa(s,t);
	while(spfa(s,t)) {
		int mx = inf;
		for(int i = pre[t]; i != -1; i = pre[edg[i ^ 1].v]) {
		//	printf("%d\n",edg[i].v);
			mx = min(mx,edg[i].c);
		}
		for(int i = pre[t]; i != -1; i = pre[edg[i ^ 1].v]) {
			edg[i].c -= mx;
			edg[i ^ 1].c += mx;
			cost += mx * edg[i].w;
		}
		//printf("%d\n",mx);
		ans += mx;
	}	
	return ans;
}
void dfs(int s,int t,int sz) {
	//printf("%d ",s);
	vis[s] = 1;
	if(s == t) return;
	for(int i = 0; i < g[s].size(); i++) {
		int v = g[s][i];
		if(!vis[v]) {
			vis[v] = 1;
			a[sz].push_back(v);
			dfs(v,t,sz);
			break;
		}
	}
}
int main() {
	init();
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++) {
		scanf("%s",tmp[i]);
		mp[tmp[i]] = i;
	}
	for(int i = 1; i <= m; i++) {
		scanf("%s%s",&s1,&s2);
		int u = mp[s1],v = mp[s2];
		if(u > v) swap(u,v);
		if(u == 1 && v == n) {
			add(u + n,v,2,1);
			add(v,u + n,0,-1);
		}
		else {
			add(u + n,v,1,1);
			add(v,u + n,0,-1);			
		}
	}
	for(int i = 1; i <= n; i++) {
		if(i == 1 || i == n) {
			add(i,i + n,2,0);
			add(i + n,i,0,0);
		}
		else {
			add(i,i + n,1,0);
			add(i + n,i,0,0);
		}
	}
	int cost,f;
	f = maxflow(1,n,cost);
	if(f != 2)
		puts("No Solution!");
	else {
		printf("%d\n",cost);
		for(int i = 1; i <= n; i++)
			for(int j = head[i + n]; j + 1; j = edg[j].nxt) {
				int c = edg[j].c;
				if(!c && i != edg[j].v)
					g[i].push_back(edg[j].v);
			}
		memset(vis,0,sizeof vis);
		printf("%s\n",tmp[1]);
		dfs(1,n,0);
		for(int i = 0; i < a[0].size(); i++)
			printf("%s\n",tmp[a[0][i]]);
		dfs(1,n,1);
		for(int i = a[1].size() - 1; i >= 0; i--)
			printf("%s\n",tmp[a[1][i]]);		
		printf("%s\n",tmp[1]);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值