Floyd求最小環
Floyd求最小環
一、演算法描述
在一張有環的圖中,通過 $O(n^3)$ 的時間效率求出邊長最小的環
二、原理與正確性推導
前置知識:
$\text{Floyd}$ 正確性推導:
Floyd的遞推公式如下
$$
\dp[i][j]=\min_{1 \le k \le n}(d[i][k]+d[k][j])
$$
所以,當 $dp[i][k]$ 和 $dp[k][j]$ 都取最小值時,由公式珂知 $dp[i][j]$ 會取到最小值
證明:設 $i$ 和 $j$ 之間的最短路徑上的節點集中(不包含 $i$ 、 $j$ ),編號最大為 $x$
$$
\therefore 當k=x時,d[i][j]一定能得到最小值
$$
$$
用強歸納法證明
$$
$$
設i到x中間編號最大的是x_1,x到j中間編號最大的是x_2
$$
$$
\because x是i到j中間編號最大的
$$
$$
\therefore x_1\lt x,x_2\lt x
$$
$$
\therefore 根據結論,k=x_1時,d[i][k]已取得最小值
$$
$$
k=x_2時d[x][j]已取得最小值
$$
$$
\therefore 當k=x時,d[i][k]和d[k][j]都已取得最小值
$$
$$
\therefore 當k=x時,d[i][j]能取得最小值
$$
$$
\tiny Q.E.D
$$
既然已經證明了上面的公式是正確的,那麼根據公式寫出的 $Floyd$ 演算法也應是正確的
我們首先寫出 $Floyd$ 的核心程式碼
for(int k=1;k<=n;k++)//以k為中轉點
for(int i=1;i<=n;i++)//i為起點
for(int j=1;j<=n;j++)//j為終點
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
對這個演算法進行分析珂知
在 $Floyd$ 演算法列舉第 $k$ 箇中轉點時,已經得到了前 $k-1$ 箇中轉點的最短路徑
這 $k-1$ 個點不包括點 $k$ ,並且他們的最短路徑也不經過點 $k$ (排除起點、終點)
那麼我們從前 $k-1$ 個點中選取兩個點 $i,j$
由上珂知:由 $i$ 到 $j$ 的最短路徑已經求出
如果 $j$ 能到 $k$ ,且 $k$ 能到 $i$
那麼連線 $i-j-k-i$ ,我們就得到了包含 ${i,j,k}$ 三個點的最小環
最後列舉所有用這個方法算出的最小環,求最小即珂
三、例題及程式碼
由於題目中的 $1\le n\le 100$ ,我們珂以用時間效率為 $O(n^3)$ 的 $Floyd$ 來求最小環
核心程式碼:
for(int k=1;k<=n;k++){
for(int i=1;i<k;i++){
for(int j=i+1;j<k;j++){
ans=min(ans,dis[i][j]+a[j][k]+a[k][i]);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
dis[j][i]=dis[i][j];
}
}
}
AC程式碼:雲剪貼簿
四、優缺點
優點
程式碼簡單,珂以直接當作模板背下來
缺點
$O(n^3)$ ,大部分題都沒戲
同時因為使用鄰接表存圖,所以記憶體也堪憂