單源最長路徑怎麼求 ?當然就是把單源最短路徑改過來就行了.
那麼,其他涉及邊的狀態轉移的問題呢?是否也能用最短路算法魔改而成?答案是肯定的.
題目大意
給定一張無向圖和一個終點,定義一個點到終點的權爲這個點到終點的所有路徑中長度最大的邊的最小值.
如圖,點0到4的路徑有三條,它們的路徑上的邊權最大值分別是8,9,7,取最小,輸出7
題目分析
涉及邊之間狀態的轉移,前後狀態又互相有關聯,顯然可以用最短路算法的拓展變式求解.
而我們知道,狀態轉移通過鬆弛實現.
爲了表示這種狀態的轉移,我們不妨分析一下這個最小值是怎麼通過鬆弛得到的,以得到鬆弛的實現思路.
原始圖
點爲最終要到達的點,由於是無向圖,所以從它開始做最短路(倒着路徑訪問順序求所求邊)也是沒問題的.
第一次鬆弛之後
記表示當前點到點的路徑的所求邊.
第二次鬆弛(1)
現在到的所求邊爲
第二次鬆弛(2)
現在到的所求邊爲
那麼,我們就可以得到鬆弛時的操作:
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;
}
題後總結
由於各種最短路算法,實際上都是邊之間的狀態轉移,所以所有涉及邊之間狀態轉移(前後狀態有關聯)的有關圖的問題,都可以在最短路算法的基礎上加以修改,從而得解.