大概是倍增Floyd的模板題。
Floyd的原理是一個一個點向圖中加,k表示已經加了k個點,並且加點可以累加。(可以參考上篇博文)而這道題恰是強制加入T個點,那麼我們可以將T利用一種類似於加速冪的思想向其中加點。而需要注意的是,兩個數組的合併需要第三個輔助數組維護,輔助數組初值應設爲無限大,用已知的兩個數組去更新輔助數組,最後將輔助數組中的值賦給答案數組。
還需要注意,答案數組一開始除了自己到自己,其他的全賦爲無限大,因爲代價爲0時每個節點可以到達的地方僅爲其本身。
其實這道題本身可以用矩陣來理解,矩陣具有結合律,本題的一種路徑相加也滿足結合律,將矩陣乘法的定義改爲取min即可(大概也是理解倍增Floyd的一種思路)。
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=205;
struct edge
{
int x,y,val;
}e[maxn];
int n,cnt;
int disc[maxn<<3],map[maxn][maxn],dist[maxn][maxn],temp[maxn][maxn];
void floyd(int a[][maxn],int b[][maxn])
{
memset(temp,0x3f,sizeof temp);//合併兩種狀態,必須賦初值以最大值
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
temp[i][j]=min(temp[i][j],a[i][k]+b[k][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=temp[i][j];
}
int main()
{
int k,m,S,T;z
scanf("%d%d%d%d",&k,&m,&S,&T);
memset(map,0x3f,sizeof map);
memset(dist,0x3f,sizeof dist);
for(int i=1,u,v,val;i<=m;i++)
{
scanf("%d%d%d",&val,&u,&v);
e[i].x=u;e[i].y=v;e[i].val=val;
disc[++cnt]=u;disc[++cnt]=v;
}
sort(disc+1,disc+cnt+1);
n=unique(disc+1,disc+cnt+1)-(disc+1);
S=lower_bound(disc+1,disc+n+1,S)-disc;
T=lower_bound(disc+1,disc+n+1,T)-disc;
//路徑爲0時花費均爲0,所以初始狀態自身到自身dist不賦值
for(int i=1;i<=n;i++)dist[i][i]=0;
for(int i=1;i<=m;i++)
{
int x=lower_bound(disc+1,disc+n+1,e[i].x)-disc;
int y=lower_bound(disc+1,disc+n+1,e[i].y)-disc;
map[x][y]=map[y][x]=e[i].val;
}
while(k)
{
if(k&1)floyd(dist,map);
floyd(map,map);
k>>=1;
}
printf("%d",dist[S][T]);
return 0;
}