单源最长路径怎么求 ?当然就是把单源最短路径改过来就行了.
那么,其他涉及边的状态转移的问题呢?是否也能用最短路算法魔改而成?答案是肯定的.
题目大意
给定一张无向图和一个终点,定义一个点到终点的权为这个点到终点的所有路径中长度最大的边的最小值.
如图,点0到4的路径有三条,它们的路径上的边权最大值分别是8,9,7,取最小,输出7
题目分析
涉及边之间状态的转移,前后状态又互相有关联,显然可以用最短路算法的拓展变式求解.
而我们知道,状态转移通过松弛实现.
为了表示这种状态的转移,我们不妨分析一下这个最小值是怎么通过松弛得到的,以得到松弛的实现思路.
原始图
s s s点为最终要到达的点,由于是无向图,所以从它开始做最短路(倒着路径访问顺序求所求边)也是没问题的.
第一次松弛之后
记 l e n len len表示当前点到 s s s点的路径的所求边.
第二次松弛(1)
现在 k k k到 s s s的所求边为 l e n = m a x ( 8 , 10 ) = 10 len=max(8,10)=10 len=max(8,10)=10
第二次松弛(2)
现在 k k k到 s s s的所求边为 l e n = m i n ( l e n , m a x ( 2 , 7 ) ) = 7 len=min(len,max(2,7))=7 len=min(len,max(2,7))=7
那么,我们就可以得到松弛时的操作:
1.若当前节点没有len,那么len=max(len[u],e.w)
2.否则当前节点的len=min(len,max(len[u],e.w))
其中u是前一个节点,e.w是u到当前节点的边权
全题就解决了.
程序实现
#include<bits/stdc++.h>
#define maxn 50010
using namespace std;
struct edge{
int next,v,w;
}e[maxn];
int head[510],tot;
void add(int u,int v,int w){
e[++tot].v =v;
e[tot].w =w;
e[tot].next =head[u];
head[u]=tot;
}
int n,m,s;
bool vis[510];
int len[510];
void spfa(){
memset(len,-1,sizeof len);//-1为初始状态
memset(vis,false,sizeof vis);
queue<int >q;
q.push(s);
vis[s]=true;
len[s]=0;
while(!q.empty()){
int u=q.front();
for(int i=head[u];i;i=e[i].next ){
int v=e[i].v ;
if(len[v]==-1){
len[v]=max(len[u],e[i].w );
vis[v]=true;
q.push(v);
}//如果还是初始状态,直接更新
if(len[v]>max(len[u],e[i].w )){
len[v]=max(len[u],e[i].w );
if(!vis[v]){
vis[v]=true;
q.push(v);
}//否则进行比较然后视情况松弛
}
}
vis[u]=false;
q.pop();
}
}
int main(){
int T;
scanf("%d",&T);
for(int ab=1;ab<=T;ab++){
memset(e,0,sizeof e);
memset(head,0,sizeof head);//初始化
tot=0;
scanf("%d%d",&n,&m);
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);//无向图
}
scanf("%d",&s);
spfa();
printf("Case %d:\n",ab);
for(int i=0;i<n;i++){
if(len[i]==-1)printf("Impossible\n");
else printf("%d\n",len[i]);
}}
return 0;
}
题后总结
由于各种最短路算法,实际上都是边之间的状态转移,所以所有涉及边之间状态转移(前后状态有关联)的有关图的问题,都可以在最短路算法的基础上加以修改,从而得解.