前言:
本系列是学习了董晓老师所讲的知识点做的笔记
董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com)
动态规划系列(还没学完)
【董晓算法】动态规划之线性DP问题-CSDN博客
【董晓算法】动态规划之背包DP问题(2024.5.11)-CSDN博客
【董晓算法】动态规划之背包DP与树形DP-CSDN博客
字符串系列()
【董晓算法】竞赛常用知识之字符串1-CSDN博客
【董晓算法】竞赛常用知识之字符串2-CSDN博客
数据结构系列(未学完)
【董晓算法】竞赛常用知识点之数据结构1-CSDN博客
搜索系列
[董晓算法]搜索相关题目及模板-CSDN博客
拓扑排序
Kahn(卡恩)算法(这个好理解)
bool toposort(){
queue<int> q;
for(int i = 1; i <= n; i++)
if(din[i]==0) q.push(i);
while(q.size()){
int x=q.front(); q.pop();
tp.push_back(x);
for(auto y : e[x]){
if(--din[y]==0) q.push(y);
}
}
return tp.size() == n;
}
int main(){
cin >> n >> m;
for(int i=0; i<m; i++){
cin >> a >> b;
e[a].push_back(b);
din[b]++;
}
if(!toposort()) puts("-1");
else for(auto x:tp)printf("%d ",x);
return 0;
}
DFS 算法
vector<int> e[N], tp;
int c[N]; //染色数组
bool dfs(int x){
c[x] = -1;
for(int y : e[x]){
if(c[y]<0)return 0; //有环
else if(!c[y])
if(!dfs(y))return 0;
}
c[x] = 1;
tp.push_back(x);
return 1;
}
bool toposort(){
memset(c, 0, sizeof(c));
for(int x = 1; x <= n; x++)
if(!c[x])
if(!dfs(x))return 0;
reverse(tp.begin(),tp.end());
return 1;
}
最短路
Dijkstra算法
每次
Dijkstra(迪杰斯特拉)算法是基于贪心思想的单源最短路算法
暴力法
添边
struct edge { int v, w; };
vector<edge>e[N];
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
e[a].push_back({ b,c });
}
核心代码
void dijkstra(int s) {
for (int i = 0; i <= n; i++) d[i] = inf;
d[s] = 0;//原点设为0
for (int i = 1; i < n; i++) {
int u = 0;
for (int j = 1; j <= n; j++)//枚举每一个点
if (!vis[j] && d[j] < d[u]) u = j;
vis[u] = 1;//标记
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
}
}
}
}
代码
struct edge { int v, w; };
vector<edge>e[N];
int d[N], vis[N];
void dijkstra(int s) {
for (int i = 0; i <= n; i++) d[i] = inf;
d[s] = 0;//原点设为0
for (int i = 1; i < n; i++) {
int u = 0;
for (int j = 1; j <= n; j++)//枚举每一个点
if (!vis[j] && d[j] < d[u]) u = j;
vis[u] = 1;//标记
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
}
}
}
}
int main() {
cin >> n >> m >> s;
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
e[a].push_back({ b,c });
}
dijkstra(s);
for (int i = 1; i <= n; i++)
printf("%d ", d[i]);
return 0;
}
Heap-Dijkstra堆优化版
用优先队列维护被更新的点的集合O(mlogm)
创建一个pair类型的大根堆q{-距离,点),把距离取负值,距离最小的元素在堆顶
void dijkstra(int s) {
for (int i = 0; i <= n; i++) d[i] = inf;
d[s] = 0;//原点设为0
q.push({0, s});
/*for (int i = 1; i < n; i++) {
int u = 0;
for (int j = 1; j <= n; j++)//枚举每一个点
if (!vis[j] && d[j] < d[u]) u = j;
vis[u] = 1;//标记
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
}
}
}*/
while (q.size()) {
auto t = q.top(); q.pop();
int u = t.second;
if (vis[u]) continue;
vis[u] = 1;
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
q.push({-d[v], v});
}
}
}
}
路径记录与递归输出
红颜色为与上述堆化版不同处
Bellman-Ford 算法
P3385 【模板】负环 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
bool bellmanford() {
memset(d, inf, sizeof d); d[s] = 0;//起点到起点距离设为0
bool flag; //是否松弛
for (int i = 1; i <= n; i++) { //n轮
flag = false;
for (int u = 1; u <= n; u++) { //n个点
if (d[u] == inf)continue;
for (auto ed:e[u]) {
int v = ed.v,w=ed.w;
if (d[v] > d[u] + w[j]) {
d[v] = d[u] + w[j];
flag = true;
}
}
}
if (!flag)break;
}
return flag; //有负环
}
Bellman-Ford 算法的优化-SPFA
bool spfa(int s) {
memset(d, inf, sizeof d);
d[1] = 0, vis[1] = 1; q.push(1);
while (q.size()) {
int u = q.front(); q.pop(); vis[u] = 0;
for (auto ed:e[u]) {
int v = ed.v, w = ed.w;
if (d[v] > d[u] + w[i]) {
d[v] = d[u] + w[i];
cnt[v] = cnt[u] + 1;//记录边数
if (cnt[v] >= n)return true;//有负环
if (!vis[v])q.push(v), vis[v] = 1;
}
}
}
return false;
}
算法对比
Floyd算法
void floyd(){
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
路径的记录与递归输出
和二叉树的遍历方式类似