洛谷P2149 [SDOI2009]Elaxia的路线(最短路,拓扑排序)

时间:2022-09-13 22:08:46

洛谷P2149 [SDOI2009]Elaxia的路线(最短路,拓扑排序)

题目描述

最近,Elaxia和w的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间。Elaxia和w每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。 现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有N个路 口,M条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

输入输出格式

输入格式:

第一行:两个整数N和M(含义如题目描述)。 第二行:四个整数x1、y1、x2、y2(1 ≤ x1 ≤ N,1 ≤ y1 ≤ N,1 ≤ x2 ≤ N,1 ≤ ≤ N),分别表示Elaxia的宿舍和实验室及w**的宿舍和实验室的标号(两对点分别 x1,y1和x2,y2)。 接下来M行:每行三个整数,u、v、l(1 ≤ u ≤ N,1 ≤ v ≤ N,1 ≤ l ≤ 10000),表 u和v之间有一条路,经过这条路所需要的时间为l。

输出格式:

一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)

输入输出样例

输入样例:

9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1

输出样例:

3

说明

对于30%的数据,N ≤ 100;
对于60%的数据,N ≤ 1000;
对于100%的数据,N ≤ 1500,输入数据保证没有重边和自环。

解题分析:

首先想到的是分别求x1到y1的最短路径和x2到y2的最短路径,然后统计这两条路径上重复边的权重和。
但是这显然是不对的,因为两个点的最短路径的长度是唯一的,但最短路径可能会有多条,题目的意思是两人都选择恰当的路径,使得他们在一起的时间最多。至于当确定最长公共路径后他们能不能同时到达该公共路径或者在一个公共路径上他们所走的方向相反等具体的操作可行性则不需要考虑,关键注意输出格式要求的最后一句:最长公共路径的长度。
如果求x1到y1的所有的最短路径,以及x2到y2的最短路径,再求每一对最短路径之间的公共路径的长度,最后取这些长度的最大值,可能会超时。
预备知识:
1、如果从x1到y1的两条最短路径都经过某个顶点k,则两条路径从x1到k的距离肯定相等,从k到y1的距离也相等。
2、假设x1到y1的最短距离为xy1,则当某条边i(起点为xi,终点为yi,边长为w)满足公式1:d(x1, xi) + w + d(yi, y1)=xy1(d(x, y)为x到y的最短距离)时,则边i一定在x1到y1的某条最短路径上。
解决方法:
1、先确定从x1到y1的所有可能的最路径上的边。可以用公式1,因此必须预先得到xy1,d(x1, xi), d(yi, y1),即x1到y1的最短距离,x1到任一点的最短距离,y1到任一点的最短距离。这可以分别从x1和y1出发,利用Dijkstra或SPFA计算。
2、判断x1和y1的最短路径上的边是否在x2到y2的某条最短路上。同样利用公式1,因此再进行两次Dijkstra或SPFA算法。在所有边上添加一条属性flag,如果是上面所述的公共边,则flag=1,否则,flag=0
3、将1中所得到的边构造一个有向图,方向是从x1出发到达y1的行走方向。
4、对所构造的有向图,根据预备知识1,利用拓扑排序算法求每个顶点的cmlen[i](表示到达该定点的每一个路径所经过边的权重和的最大值)。这里边的权重稍作调整:原来边的权重*该边的flag,即那些与x2到y2的某个最短路径重合的边有非零权,其他都是0权。

5、最后的结果为cmlen[y1]。

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define N 1503
#define INF 0x7fffffff / 2
int n, m, xy[4], xy1, xy2, dis[N][4], vis[N], ans = 0;
struct node{
	int from;
	int to;
	int next;
	int v;	
	int flag; 
	node():next(-1), v(INF), flag(0){
	}
}e[N*N], e1[N*N];
int head[N], cnt = 0, head1[N], cnt1 = 0, indegree[N], cmlen[N] = {0};

void get_i(int &x){
	char ch = getchar();
	x = 0;
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)){
		x = x * 10 + ch - '0';
		ch = getchar();
	}
}

void add_e(int from, int to, int v){
	e[cnt].from = from;
	e[cnt].to = to;
	e[cnt].v = v;
	e[cnt].next = head[from];
	head[from] = cnt++;
}

void add_e1(int from, int to, int v, int f){	
	e1[cnt1].from = from;
	e1[cnt1].to = to;
	e1[cnt1].v = v;
	e1[cnt1].flag = f;
	e1[cnt1].next = head1[from];	
	head1[from] = cnt1++;
	if(indegree[to]==-1)
		indegree[to] = 1;
	else
		indegree[to]++;
	if(indegree[from]==-1)
		indegree[from] = 0;
}

void spfa(int start, int index){
	int i, j, k, v;
	queueq;
	q.push(start);
	dis[start][index] = 0;
	while(!q.empty()){
		k = q.front();
		q.pop();
		vis[k] = 0;
		for(i=head[k]; ~i; i = e[i].next){
			j = e[i].to, v = e[i].v;
			if(dis[j][index] > dis[k][index] + v){
				dis[j][index] = dis[k][index] + v;
				if(!vis[j]){
					q.push(j);
					vis[j] = 1;
				}
			}
		}		
	}
}

void topsort(){
	int i, j, k, i1, tmp, tmp1;
	queueq;
	for(i=1; i<=n; i++){
		if(indegree[i]==0){  // 其实只有一个点(xy[0])满足该条件 
			q.push(i);			
		}			
	}
	while(!q.empty()){
		k = q.front();
		q.pop();		
		for(i=head1[k]; ~i; i = e1[i].next){
			j = e1[i].to;
			indegree[j]--;
			if(indegree[j]==0){
				q.push(j);				
			}
			// 更新到第j个顶点为止,最长公共路径的长度 
			cmlen[j] = max(cmlen[j], cmlen[k]+e1[i].v*e1[i].flag);						
		}
	}
}

int main(){
	ios::sync_with_stdio(false);
	int i, j, a, b, c, flag;
	node n1;
	get_i(n), get_i(m);
	for(i=0; i<4; i++)
		get_i(xy[i]);
	memset(head, -1, sizeof(head));
	for(i=0; i