题意:从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; }
时间: 2024-10-05 23:25:31