"Shortest" pair of paths[题解]

"Shortest" pair of paths

题目大意

给出 \(n\) 个点,\(m\) 条边,除第一个点和最后一个点外,其他所有的点都只能被经过一次,要求找到两条从第一个点到最后一个点的路径,使其长度和最小。

分析

拿到这道题,通过观察数据范围和问题模式,其实很容易想到这是一道可以通过网络流来解决的问题,网络流确实非常擅长通过连边的流量限制来表示这种对经过次数的限制

本题就是一个非常明显的最小费用流,其建图套路也比较容易想到:

  • 拆点,将每个点拆成入点和出点,除了第一个点与最后一个点,点 \(i\)\(i'\) 间建立一条流量为 \(1\) 、 费用为 \(0\) 的边,而点 \(1\) 与点 \(n\) 则需要建立一条流量为 \(2\) 的边,费用为 \(0\)

  • 建立一个超级源点和一个超级汇点,超级源点向点 \(1\) 连接一条流量为 \(+\infty\) 、费用为 \(0\) 的边,同理,点 \(n\) 向超级汇点建立一条流量为 \(+\infty\) 、费用为 \(0\) 的边。

  • 对于给出的每一条边,设由 \(i\) 通往 \(j\) ,花费为 \(val\) ,则由 \(i'\)\(j\) 连接一条流量为 \(1\) ,费用为 \(val\) 的边。

思考这样做的正确性,拆点显然维护了对于每个点只能被经过一次的性质,不难发现这样找出来的两条路径除了点 \(1\) 与点 \(n\) 外经过的其他点都一定不会重复,与题意相符,这样建出来的图配上最小费用流的模板是能够通过这道题的。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=2e3+10,M=2e4+10,INF=0x3f3f3f3f;
int T,n,m,s,_s,t,maxf,maxc;
inline int read()
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int tot=-1,v[2*M+4*N],w[2*M+4*N],p[2*M+4*N],nex[2*M+4*N],first[2*M+4*N];
inline void Add(int x,int y,int z,int c)
{
	nex[++tot]=first[x];
	first[x]=tot;
	v[tot]=y,w[tot]=z,p[tot]=c;
}
bool vis[2*N];
int pre[2*N],dis[2*N],Min[2*N]; 
inline bool SPFA()
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,false,sizeof(vis));
	queue<int> q;
	q.push(s);
	vis[s]=true,dis[s]=0,Min[s]=INF;
	while(!q.empty()){
		int now=q.front(); q.pop();
		vis[now]=false;
		for(register int i=first[now];i!=-1;i=nex[i]){
			int to=v[i];
			if(!w[i]) continue;
			if(dis[to]>dis[now]+p[i]){
				dis[to]=dis[now]+p[i];
				Min[to]=min(Min[now],w[i]);
				pre[to]=i;
				if(!vis[to]) q.push(to),vis[to]=true;
			}
		}
	}
	return dis[t]!=INF;
}
inline void EK()
{
	while(SPFA()){
		maxf+=Min[t];
		maxc+=dis[t]*Min[t];
		int temp=t,i;
		while(temp!=s){
			i=pre[temp];
			w[i]-=Min[t];
			w[i^1]+=Min[t];
			temp=v[i^1];
		}
	}
}
int main()
{
	while(1){
		T++;
		memset(first,-1,sizeof(first));
		tot=1;maxf=maxc=0;
		n=read(),m=read();
		if(!n&&!m) break;
		s=0,t=2*n+1;
		Add(s,1,INF,0),Add(1,s,0,0);
		Add(1,1+n,2,0),Add(1+n,1,0,0);
		Add(n,2*n,2,0),Add(2*n,n,0,0);
		Add(2*n,t,INF,0),Add(t,2*n,0,0); 
		for(register int i=1;i<=m;i++){
			int x=read(),y=read(),z=read();
			x++,y++; 
			Add(x+n,y,1,z),Add(y,x+n,0,-z);
		}
		for(register int i=2;i<n;i++) Add(i,i+n,1,0),Add(i+n,i,0,0);
		EK();
		if(maxf!=2) printf("Instance #%d: Not possible\n",T);
		else printf("Instance #%d: %d\n",T,maxc);
	}
	
	return 0;
}
posted @ 2021-03-03 21:51  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(51)  评论(0编辑  收藏  举报