【BZOJ】1706: [usaco2007 Nov]relays 奶牛接力跑

【题意】给定m条边的无向图,起点s,终点t,要求找出s到t恰好经过n条边的最短路径。n<=10^6,m<=100。

【算法】floyd+矩阵快速幂

【题解】

先对点离散化,得到点数N。

对初始边建立初始矩阵,然后考虑每次多跑一条边相当于一次矩阵乘法,即c[i][j]=min(a[i][k],a[k][j]),k=1~N。

定义了矩阵乘法,就可以用矩阵快速幂优化了。

初始矩阵ans[i][i]=0,转移矩阵a[i][i]=inf,这样就是恰好n条边。(如果a[i][i]=0就是<=n条边)

复杂度O(N^3*log n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=210;//Òª¿ªÁ½±¶¡£ 
typedef int mat[N][N];
mat a,c,ans;
int n,m,s,t,cnt=0,z[1010];
int read()
{
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
void mul(mat a,mat b){
    memset(c,0x3f,sizeof(c));
    for(int i=1;i<=cnt;i++){
        for(int j=1;j<=cnt;j++){//iºÍjÒ»ÑùÒ²ÊÇ¿ÉÒÔÈÆһȦ»ØÀ´µÄ¡£ 
            for(int k=1;k<=cnt;k++)c[i][j]=min(c[i][j],a[i][k]+b[k][j]);
        }
    }
    for(int i=1;i<=cnt;i++)for(int j=1;j<=cnt;j++)a[i][j]=c[i][j];
}
int main(){
    n=read();m=read();s=read();t=read();
    memset(a,0x3f,sizeof(a));
    for(int i=1;i<=m;i++){
        int w=read(),u=read(),v=read();//ȨֵÔÚµÚһλ£¡£¡£¡ 
        if(!z[u])z[u]=++cnt;if(!z[v])z[v]=++cnt;
        u=z[u];v=z[v];
        a[u][v]=a[v][u]=min(a[u][v],w);
    }
    memset(ans,0x3f,sizeof(ans));
    for(int i=1;i<=cnt;i++)ans[i][i]=0;
    while(n){
        if(n&1)mul(ans,a);
        mul(a,a);
        n>>=1;
    }
    printf("%d",ans[z[s]][z[t]]);
    return 0;
}    
View Code
原文地址:https://www.cnblogs.com/onioncyc/p/7590255.html