zoukankan      html  css  js  c++  java
  • P2149 [SDOI2009]Elaxia的路线[最长公共路径]

    题目描述

    最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间。

    Elaxia和w**每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。

    现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有N个路 口,M条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

    解析

    这个题,怎么说呢,对我来说思维难度还是比较低的,但是代码难度稍微就高了点。

    题目清楚的告诉我们要求两对点之间最短路的最大公共路径。实际上两点之间的最短路可能有多条,于是我们就要把它们都标出来。题目还提到希望在节约时间的前提下,一起走的时间尽可能的长,因此我们并不一定需要两人都只走各自的最短路,我们只需要让最终答案最优即可(即不能再更节约时间,一起走的时间不能再更长)。为了更好表示,我们引入一些符号。

    (x1,y1)之间所有最短路的边的集合为(A)(x2,y2)之间所有最短路的边的集合为(B)。我们首先要做的是找出这两个集合,然后比对(A)(B),找出一个集合(Cin A cap B),它就是两点对所有最短路径的公共边。

    此时,我们只需要在(C)中找出一条最长的简单路径即可。为什么是简单路径呢,因为最短路本身就是简单路径嘛(废话)。


    首先标记最短路上的边很容易,我们分别从起点终点两端点跑两遍最短路算法,然后再对每条边进行检查。假设起点为(s),终点为(t),对于某个点(x),它扩展出的点为(y),它们之间的边(c(x,y)=z)(d[x])为到(x)点的最短路,如果有(d[x]+d'[y]+z=d[t]),那么这条边在(s ightarrow t)最短路上。所以两对点我们要跑四次。注意,如果我们不跑两遍最短路的话,该方法会标记所有可达边。


    所以接下来怎么找(C)中的最长简单路径呢?说实话我没想出来。

    一开始打算在第二轮最短路时搞一个类似递推的东西统计。显然对于某个点(y),它可以由很多个点扩展而来,而我们只选取其中使得最长公共路径最大的一个点(x)扩展过来(当然首先这条边(c(x,y))得在(C)中)。然而本人能力实在有限,并没有没码出来(就是菜)。看到题解里似乎也有人这么写,并且过了,我枯了。


    遂看了题解,震惊,竟然可以用拓扑排序?!(我才不写什么狗屁重载呢,麻烦的很)

    我还在纳闷无向图怎么搞拓扑的时候,人家已经想到分析两人同方向走和反方向走两种情况了!意思就是说我们可以考虑两人同方向和反方向走两种情况的最长公共路径,妙哉!

    因此我们需要对两张图进行拓扑排序找最长链:(x1 ightarrow y1)所有最短路上的边按照(x1 ightarrow y1)的方向存,(x2 ightarrow y2)的所有最短路上的边按照(x2 ightarrow y2)的方向存的图1;(x1 ightarrow y1)所有最短路上的边按照(x1 ightarrow y1)的方向存,(y2 ightarrow x2)的所有最短路上的边按照(y2 ightarrow x2)的方向存的图2,注意这些最短路边包含在(C)中。

    最后在两张图的最优解中择优。

    神奇。

    参考代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define N 3010
    using namespace std;
    inline int read()
    {
    	int f=1,x=0;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    struct rec{
    	int next,ver,edge;
    }g[N*N],G[N*N];
    int head[N],tot,headG[N],totG,n,m,x1,x2,y1,y2;
    int d[4][N],ing[N],c[N];
    bool v[N];
    priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    inline void add(int x,int y,int val)
    {
    	g[++tot].ver=y,g[tot].edge=val;
    	g[tot].next=head[x],head[x]=tot;
    }
    inline void addG(int x,int y,int val)
    {
    	G[++totG].ver=y,G[totG].edge=val;
    	G[totG].next=headG[x],headG[x]=totG;
    	ing[y]++;
    }
    inline void dijkstra(int k,int x)
    {
    	memset(d[k],0x3f,sizeof(d[k]));
    	memset(v,0,sizeof(v));
    	d[k][x]=0;q.push(make_pair(0,x));
    	while(q.size()){
    		int index=q.top().second;q.pop();
    		if(v[index]) continue;
    		v[index]=1;
    		for(int i=head[index];i;i=g[i].next){
    			int y=g[i].ver,z=g[i].edge;
    			if(d[k][y]>d[k][index]+z){
    				d[k][y]=d[k][index]+z;
    				q.push(make_pair(d[k][y],y));
    			}
    		}
    	}
    }
    inline void sign(int k)
    {
    	for(int i=1;i<=n;++i)
    		for(int j=head[i];j;j=g[j].next){
    			int y=g[j].ver,z=g[j].edge;
    			if(d[0][i]+z+d[2][y]==d[0][y1]&&d[1][i]+z+d[3][y]==d[1][y2]&&k==1)
    				addG(i,y,z);
    			if(d[0][i]+z+d[2][y]==d[0][y1]&&d[3][i]+z+d[1][y]==d[1][y2]&&k==2)
    				addG(i,y,z);
    		}
    }
    int main()
    {
    	n=read(),m=read();
    	x1=read(),y1=read(),x2=read(),y2=read();
    	for(int i=1;i<=m;++i){
    		int u,v,val;
    		u=read(),v=read(),val=read();
    		add(u,v,val),add(v,u,val);
    	}
    	dijkstra(0,x1),dijkstra(1,x2);
    	dijkstra(2,y1),dijkstra(3,y2);
    	//topsort
    	int ans=0;
    	queue<int> Q;
    	sign(1);
    	for(int i=1;i<=n;++i)
    		if(ing[i]==0) Q.push(i);
    	while(Q.size()){
    		int x=Q.front();Q.pop();
    		for(int i=headG[x];i;i=G[i].next){
    			int y=G[i].ver,z=G[i].edge;
    			if(--ing[y]==0){
    				c[y]=max(c[x]+z,c[y]);
    				ans=max(ans,c[y]);
    				Q.push(y);
    			}
    		}
    	}
    	memset(ing,0,sizeof(ing));
    	memset(headG,0,sizeof(headG));
    	memset(c,0,sizeof(c));
    	sign(2);
    	for(int i=1;i<=n;++i)
    		if(ing[i]==0) Q.push(i);
    	while(Q.size()){
    		int x=Q.front();Q.pop();
    		for(int i=headG[x];i;i=G[i].next){
    			int y=G[i].ver,z=G[i].edge;
    			if(--ing[y]==0){
    				c[y]=max(c[x]+z,c[y]);
    				ans=max(ans,c[y]);
    				Q.push(y);
    			}
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    模拟器分辨率收集
    6. 蜂鳴器
    5. 伺服馬達
    4. 流水燈
    3. 序列埠的輸出以及按鈕消抖
    2. 沒有按鈕的電路是沒有靈魂的
    1. 點亮一個LED燈
    ARDUINO準備工作
    vritualenvwrapper的基本操作
    蠻力法兌換零錢及其優化
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11429091.html
Copyright © 2011-2022 走看看