1. 程式人生 > 實用技巧 >[USACO07NOV]Cow Relays G

[USACO07NOV]Cow Relays G

題目大意

給出一張無向連通圖(點數小於1000),求S到E經過k條邊的最短路。

演算法

這是之前國慶模擬賽的題
因為懶 所以就只挑一些題寫部落格
在考場上寫了個dp 然後水到了50分 出考場和神仙們一問才知道是lyd藍書原題

我們考慮有兩個floyd的矩陣 A代表走了x條邊的矩陣 B代表走了y條邊的矩陣
那麼我們想求出C這個代表走了(x + y)這個矩陣的值呢
我們考慮這麼一個式子
\(C[i][j] = min( A[i][k] + B[k][j] )\)
然後我們發現 其中\(A[i][k] + B[k][j]\)這個式子和矩陣乘的式子很像
我們把矩陣乘的 \(+\) 改成 \(min\) 即可
那麼我們可以考慮將初始給定A矩陣(也就是走了一次的floyd矩陣)進行n - 1次轉移
\(A_n[i][j] = (A[i][j]) ^ {n - 1}\)


然後用快速冪就可以實現了

程式碼

#include <bits/stdc++.h>
using namespace std;
int num[1000005];
int n,s,t,e,tol;
struct map
{
    int a[500][500];
    map operator * (const map &x) const //過載運算子,一會兒要用
    {
        map c;
        memset(c.a,0x3f3f3f3f,sizeof(c.a));//取min,顯然置大數
        for(int k=1;k<=tol;k++)
            for(int i=1;i<=tol;i++)
                for(int j=1;j<=tol;j++)
                    c.a[i][j]=min(c.a[i][j],a[i][k]+x.a[k][j]);
        return c;		
    }
}dis,ans;
void init()
{
    memset(dis.a,0x3f3f3f3f,sizeof(dis.a));
    int x,y,z;
    cin>>n>>t>>s>>e;
    for(int i=1;i<=t;i++)
    {
        scanf("%d %d %d",&x,&y,&z);
        if(!num[y])  //這裡做一個離散化
            num[y]=++tol;
        if(!num[z])
            num[z]=++tol;
        dis.a[num[y]][num[z]]=dis.a[num[z]][num[y]]=x;
    }
}
void doit() //快速冪
{
    n--;
    ans=dis;
    while(n)
    {
        if(n&1)
            ans=ans*dis;
        dis=dis*dis;
        n>>=1;
    }
}
int main()
{
    init();
    doit();
    cout<<ans.a[num[s]][num[e]];
}