【题解】洛谷P2149 [SDOI2009]Elaxia的路线

前往:我自己搭建的博客

题目

洛谷P2149 Elaxia的路线

题解

要求最短路的公共路径,首先需要分别找出两个点对的最短路径。注意:最短路径不一定是一条 链,由于可能存在多条长度相同的最短路径,这些路径构成了一张图。考虑到起点S到终点T的最短路径和T到S的相同,所以就简化,只考虑S到T的最短路径(有向),于是,此时的最短路径就构成了一张DAG(有向无环图)。

找出最短路径的(其中一种)方法:先求出最短路径,然后用dijkstra算法求出S点到每个点的最短距离ds[](前两个步骤同时进行),同理求出T点到每个点的最短距离dt[]。枚举每一条边,假设边的两个端点是x,y(此边为有向边x->y),如果ds[x]+边权+dt[y]=最短路径长度,则该边位于S->T的最短路上;如果dt[x]+边权+ds[y]=最短路径长度,则该边位于T->S的最短路上。

拓扑排序(必备知识):在DAG上,找到入度为0的点排在序列的最前面,然后删掉这个点和其连接的边,找到下一个入度为0的点,重复上述操作。这种排序方式可以保证每个点排在它的前驱节点后面,排在它的后继节点前面。沿着DAG的拓扑序dp可以保证无后效性。

dp方式(结合下图及图下方文字理解):可以沿着其中一个点对(S1,T1)的最短路径DAG进行统计,但是可能有问题,就是统计到的公共路径不一定是另一个点对(S2,T2)的同一条最短路上的。易证:最短路DAG中的任意一条路径都是有向的链。同时,最长公共路径也必定是一条链(可用反证法证明:若是分开的两条链,显然这两条链都位于两个点对的最短路DAG上,且包含这两条链的两条最短路径在两条链之间的路径不同,这两段不同的路径,若长度相同,则都为最短路,那么两条链之间的路径也算公共最短路,若长度不同,则长的那一条不是最短路,可以局部优化成短的那一条路径)。所以,在两个最短路DAG的公共路径上,两条最短路的有向链要么方向相同(如下图:公共路径:1-4-5),要么方向相反(如下图:公共路径“1-2-3或5-6-7”)。所以,在dp时,需要同时记录当前的同向链长度和反向链长度。

配图解释:S1=1,T1=7,S2=3,T2=5,红色箭头表示S1->T1的最短路DAG,蓝色箭头表示S2->T2的最短路DAG。 在3->7->6->5这条蓝色最短路上,3->7,5->6->7分别属于两条红色最短路。

存边技巧:将无向边拆成两条有向边时,将每组边的下标定为0/1、2/3、4/5。这样可以通过^1找到组内另一条边。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2000;
const int maxm=3e6;
int c=1,n,m,s1,s2,t1,t2,ans;
int d[5][maxn],h[maxn],in[maxn],f1[maxn],f2[maxn]; 
bool v[maxn],path1[maxm],path2[maxm],inq[maxn];
//d[1/2/3/4][i]分别表示s1/t1/s2/t2到节点i的最短距离
//path1[i]=1表示编号为i的边在S1->T1的最短路DAG上,path2[]同理 
//in[i]表示S1->T1的最短路DAG上,每个点的入度 
//f1[i],f2[i]分别表示dp时,以节点i结尾的同向和反向链的长度 
//inq[i]=1表示节点i当前在队列中 
struct edge{int to,nxt,w;}e[maxm];
inline void add(int from,int to,int w)
{
	e[++c].to=to; e[c].w=w; e[c].nxt=h[from]; h[from]=c;
}
priority_queue<pair<int,int> > q;
inline void dijkstra(int s,int cur)
{
	memset(v,0,sizeof(v)); d[cur][s]=0;
	q.push(make_pair(0,s));
	while(q.size())
	{
		int x=q.top().second; q.pop();
		if(v[x]) continue; v[x]=1;
		for(int i=h[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(d[cur][y]>d[cur][x]+e[i].w) 
			{
				d[cur][y]=d[cur][x]+e[i].w;
				q.push(make_pair(-d[cur][y],y));
			}
		}
	}
}
inline void get_path1()	//找到S1->T1的最短路DAG 
{
	for(int x=1;x<=n;x++)
	{
		for(int i=h[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(d[1][x]+e[i].w+d[2][y]==d[1][t1]) path1[i]=1,in[y]++;
		}
	}
}
inline void get_path2() //找到S2->T2的最短路DAG 
{
	for(int x=1;x<=n;x++)
	{
		for(int i=h[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(d[3][x]+e[i].w+d[4][y]==d[3][t2]) path2[i]=1;
		}
	}
}
queue<int> q1; 
inline void topo_and_dp()	//在拓扑序上dp 
{
	for(int i=1;i<=n;i++) if(!in[i]) q1.push(i),in[i]=1;
	while(q1.size())
	{
		int x=q1.front(); q1.pop();
		for(int i=h[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(!path1[i]) continue;
			if(path2[i]) f1[y]=max(f1[y],f1[x]+e[i].w);
			else if(path2[i^1]) f2[y]=max(f2[y],f2[x]+e[i].w);
			ans=max(ans,max(f1[y],f2[y]));
			in[y]--; if(!in[y]&&!inq[y]) q1.push(y),inq[y]=1;  
		}				//记录inq[y]很有必要,否则会超时
	}
} 
inline void solve()
{
	dijkstra(s1,1); dijkstra(t1,2);	get_path1();
	dijkstra(s2,3); dijkstra(t2,4); get_path2();
	topo_and_dp(); printf("%d\n",ans);
}
	
int main()
{
	scanf("%d%d%d%d%d%d",&n,&m,&s1,&t1,&s2,&t2);
	for(int i=1;i<=m;i++)
	{
		int x,y,z; scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	memset(d,0x3f,sizeof(d));	//初始化 
	solve();
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zjgmartin/article/details/108415390
今日推荐