大概是倍增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;
}