目錄floyd演算法是從集合的角度出發,在\(o(n^3)\)的複雜度內,求出圖中任意兩點的最短距離
令\(f(k,i,j)\)表示成經過點\(k\)後,\(i\),\(j\)之間的最短距離
那麼就有轉移:\(f(k,i,j)=min(f(k-1,i,k)+f(k-1,k,j))\)
隨後可以發現,這樣只是用到了上個狀態,那麼我們可以簡化成:\(f(i,j)\),表示\(i,j\)之間最短的距離
常用於:
最短路傳遞閉包
找最小環
恰好經過\(k\)條邊的最短路
直徑為乙個聯通塊內任意兩點最短路中最長的距離
本題給你兩個聯通塊,讓你在兩個聯通塊各選任意一點,搭建一條邊使得兩個聯通塊合併,求新的聯通塊中的直徑最小是多少
新聯通塊中的直徑有以下幾種可能:
原想兩個聯通塊中較大的直徑,在合併後依然是新聯通塊中的直徑
連線的兩個點間的距離\(dis\)加上在各自聯通塊內能到達的距離\(d_a, d_b\):\(dis+d_a+d_b\)
所以選以上兩種情況的最大值即為答案
#include #include #include using namespace std;
constexpr int n = 200;
constexpr double inf = 1e18;
typedef pairpii;
int n;
pii q[n];
double d[n][n], maxd[n];
double f(int x, int y)
int main() ;
} for (int i = 1; i <= n; i++)
} for (int k = 1; k <= n; k++)
}} double a = 0;
for (int i = 1; i <= n; i++)
a = max(maxd[i], a);
} double b = inf;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
printf("%.6lf\n", max(a, b));
return 0;
}
本題給出關於\(n\)個字元的\(m\)個不等式關係,問你能否確定兩兩之間的關係或判斷有無矛盾
由於不等式存在傳遞性,那麼不同字母間的不等式關係可以抽象成一張由較小字母指向較大字母的有向圖
對於互不相等的字元\(a, b, c\),如果\(a且\(b,那麼我們也就間接確定了\(a,
而這個過程就是圖的傳遞閉包
。
所以在建圖的過程中,求出圖的傳遞閉包,當閉包中任意兩點之間都標記過,那麼代表
\(n\)個點之間兩兩存在關係。
當某個點和自己存在不等式關係時,代表存在矛盾
當\(m\)個不等式關係結束後還不能確定關係,說明不存在關係
用floyd演算法來傳遞閉包,是最樸素的做法
#include using namespace std;
constexpr int n = 30;
int n, m;
bool st[n], g[n][n], d[n][n];
inline void floyd()
inline int check()
return 1;
}inline char get_min()
}if (ok)
}} return 'a';
}int main()
}if (!type) puts("sorted sequence cannot be determined.");
else if (type == 2) else
} return 0;
}
優化
我們發現在本題中閉包的傳遞可以通過列舉能到達a的起點x和b能到達的重點y,
當a能到達b時:
如果存在起點x到達a和b能到達終點y,那麼x能到y
如果點x能到達點a,同樣也能到點b,
如果b能到達x,那麼a也能到達x
這樣也能完成閉包的傳遞,從\(o(n^3)\)的時間複雜度降低到\(o(n^2)\)
#include using namespace std;
constexpr int n = 30;
int n, m;
bool st[n], g[n][n];
inline int check()
return 1;
}inline char get_min()
}if (ok)
}} return 'a';
}int main()
}type = check();
if (type) u = i;}}
if (!type) puts("sorted sequence cannot be determined.");
else if (type == 2) else
} return 0;
}
本題是求最小環的路徑
求最小環
對於求最小環,我們需要對floyd演算法做出一點小變形:
在floyd演算法中,當最外層迴圈到點\(k\)時,最短路陣列中f[i,j]
表示\(i\)和\(j\)在\([1,k-1]\)的最短路徑長度。
由最小環的定義可以知道,乙個環至少有3個定點,當外層迴圈列舉到\(k\)時,該環的長度為\(dis(i,j)+f[j,k]+f[k,i]\)。這樣就可以求出最小環的長度
求路徑:
最短路徑的組成是\(f(i,k)+f(k,j)\),當乙個最短路徑\(f(i,j)\)發生更新時,那麼此時的路徑為\(i\to k\to j\),所以在求最短路的時候,要記錄下來中間節點。
隨後在求最小環的時候,通過遞迴來求出路徑
#include #include #include using namespace std;
typedef long long ll;
constexpr int n = 210, inf = 0x3f3f3f3f;
int g[n][n], d[n][n], n, m;
int middle[n][n], path[n], cnt;
void get_path(int i, int j)
int main()
int res = inf;
memcpy(d, g, sizeof g);
for (int k = 1; k <= n; k++)
}for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
}} if (cnt) else
return 0;
}
本題可以將狀態表示為\(f(k,i,j)\):經過\(k\)條邊後\(i\),\(j\)之間的最短路徑
那麼就有轉移\(f(a+b,i,j)=f(a,i,k)+f(b,k,j)\)
也就是說,對於經過\(a+b\)條邊的從\(i\)到\(j\)的最短路z[i][j]
,是經過\(a\)條邊從\(i\)到\(k\)的最短路x[i][k]
和經過\(b\)條邊從\(k\)到\(j\)的最短路y[k][j]
,即z[i][k] = min(x[i][k] + y[k][j])
,且這幾個狀態可以看作是相互獨立的。
所以\(f(k,i,j)\)就等於\(k\)個\(f(1,i,j)\)相加起來,由於圖表示在矩陣中,那麼我們可以用矩陣快速冪,快速的求出\(k\)個\(f(1,i,j)\)相加的結果
注意:
本題每個點會出現多次,且會有重遍,所以需要對點進行離散化並去最小邊
記得給matrix[i][i]
初始化成0,因為自己走到自己距離是0
#include using namespace std;
constexpr int n = 210;
unordered_mapid;
int k, t, s, e, n;
int g[n][n], res[n][n];
inline void mul(int c[n], int a[n], int b[n])
memcpy(c, tmp, sizeof tmp);
}inline void fpow()
}int main()
fpow();
printf("%d\n", res[s][e]);
return 0;
}
Floyd演算法小結
floyd演算法是一種用於尋找給定的加權圖中多源點之間最短路徑的演算法,演算法流程如下所示 1 從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。2 對於每一對頂點 u 和 v,看看是否存在乙個頂點 w 使得從 u 到 w 再到 v 比已知的路徑更短。如果是更...
蒟蒻複習之 Floyd
暴力無解演算法 入門必備 然而我發現這個簡單的演算法還有很多用法 作為複習篇不講原理 圖的要求 既可以是無向圖也可以是有向圖,邊權可以為負,但是不能存在負環 複雜度為o n 3 原理 dp思路 f i j f i k f k j void floyd 注意 1應對所建的圖進行初始化如下 void i...
總複習小結
總複習小結 1 經過一學期的程式設計學習,初步認識基礎資料型別,語句結構,可以做一些簡單的程式設計。此次複習總結了一部分的函式 程式設計技巧及易錯知識點。2 符號常量,定義後不再被賦值,習慣上用大寫字母。例 define pi 3.14 const double pi 3.1415 定義最大值 de...