题意:从s点出发到达e点且n条边的最短路是多少(可以走重复的路径)
图中点<=200,n<=1000,000
思路:folyd可以实现向路径中添边,但是这题与普通的求最短路问题不一样,比如从S到E经过X条边后就已经达到了最短路,这个时候仍然要强制用folyd再添边,尽管添边后就不是最短路了,但是要注意到添加的这边要使最短路损失最小,抓住这点用folyd可以实现强制添边的操作,所以可以从n=1的状态向n的状态转移
所以可以对原来的map进行n-1次folyd,但是n的范围太大,最坏的情况要进行1000000*200^3次运算肯定会T
这时,注意到可以用倍增法加速状态的转移,任何n都可以分解成2^i的和,每个2^i的状态都可以由2^(i-1)的状态直接求得。
也就是当前的map中每个点之间的距离表示经过了2^(i-1)条边,然后对当前的map folyd一次,就可以算出map中每个点之间经过了2^(i-1)+2^(i-1)=2^i 条边的最短路
所以用倍增法加速了状态转移
//628K 94MS
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define M 205
int n,m,st,ed;
int Hash[1005],cnt;
int mapp[M][M],tmp[M][M],n_ans[M][M];
void folyd(int a[M][M],int b[M][M],int c[M][M]){
for(int k=1;k<=cnt;k++)
for(int j=1;j<=cnt;j++)
for(int i=1;i<=cnt;i++)
if(a[j][i]>b[j][k]+c[k][i])
a[j][i]=b[j][k]+c[k][i];
}
void map_copy(int a[M][M],int b[M][M]){
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++){
a[i][j]=b[i][j];
b[i][j]=inf;
}
}
int main(){
memset(mapp,0x3f,sizeof(mapp));
memset(tmp,0x3f,sizeof(tmp));
memset(n_ans,0x3f,sizeof(n_ans));
for(int i=1;i<=200;i++){
n_ans[i][i]=0;
//注意不要让 mapp[i][i]=tmp[i][i]=0,因为要使tmp[i][i]任何时候无穷大因为自身到自身也要通过其他多个牛
//mapp[i][j]不是inf的值表示必须存在边,不能假定自身到自身是一条权为0的边
//n_ans[i][i]初始化自身到自身为0是动态规划的边界条件
}
scanf("%d%d%d%d",&n,&m,&st,&ed);
while(m--){
int val,u,v;
scanf("%d%d%d",&val,&u,&v);
if(!Hash[u]){ //对顶点离散化
Hash[u]=++cnt;
}
if(!Hash[v]){
Hash[v]=++cnt;
}
mapp[Hash[u] ][Hash[v] ]=mapp[Hash[v] ][Hash[u] ]=val;
}
while(n){
if(n&1){
folyd(tmp,n_ans,mapp);
map_copy(n_ans,tmp);
}
folyd(tmp,mapp,mapp);
map_copy(mapp,tmp);
n>>=1; //加速状态转移
}
printf("%d\n",n_ans[Hash[st] ][Hash[ed] ]);
return 0;
}