前言
算法基础课-3.4
第三章 搜索与图论(三)
共4题,知识点如下:
最小生成树:
Prim算法求最小生成树、
Kruskal算法求最小生成树。
二分图:
染色法判定二分图、
匈牙利算法—二分图的最大匹配。
掌握模版的最核心部分是:记住算法思路
不能纯记模版,效率很低,一定要记思路
可以想到啥算法,就自己把算法流程描述一遍
最小生成树
稠密图一般用朴素版Prim(主要是代码较短)
稀疏图一般用Kruskal算法(思路更简单而且代码也比堆优化版Prim少)
堆优化版Prim一般不会用到(也是适用于稀疏图)
堆优化版Prim和堆优化版Dijkstra,优化方式是一样的
所以略过,只讲朴素版Prim 和 Kruskal(实用主义,用到再学,用不到就暂时不学)
朴素版prim算法
朴素版Prim算法和Dijkstra非常相似
Dijkstra
先初始化,迭代n次,每次都找距离最近的点t(不在集合中的),然后用它更新其他点,再把它放到集合里去。
集合:(已经确定最短距离的点)
Prim
集合s:在当前连通块里的所有点
区别:
Dijkstra 是用 t 更新到起点的距离
Prim 是用 t 更新到集合的距离
t:不在集合中的距离最近的点
模板
时间复杂度是 O(n2+m), n 表示点数,m 表示边数
int n; // n表示点数
int g[N][N]; // 邻接矩阵,存储所有边
int dist[N]; // 存储其他点到当前最小生成树的距离
bool st[N]; // 存储每个点是否已经在生成树中
// 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
int prim()
{
memset(dist, 0x3f, sizeof dist);
int res = 0;
for (int i = 0; i < n; i ++ )
{
int t = -1;
for (int j = 1; j <= n; j ++ )
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
if (i && dist[t] == INF) return INF;
if (i) res += dist[t];
st[t] = true;
for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
模板题 AcWing 858. Prim算法求最小生成树
初始
随便挑一个更新到其他点的距离,然后将该点加入集合
用 2(离集合最近的点)
更新其他所有点,距离没有更短,没变化,加入到集合中
然后用3更新,最后用4,距离都没有变短,保持不变
最小生成树,最短的几条边(相等的任选一条即可)
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
// 点500,边10w,稠密图,用邻接矩阵存
const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N];
int dist[N]; // 点到集合的距离
bool st[N];
int prim() {
// 1.初始化
memset(dist, 0x3f, sizeof dist);
// 2.n次迭代
int res = 0; // 存最小生成树的树边权重之和
for (int i = 0; i < n; i++) {
// 1) 每次找到集合外的所有点中距离最小的点
int t = -1;
for (int j = 1; j <= n; j++) {
// !st[j]:集合外,t == -1:还没找到任何点, dist[t] > dist[j] : t的距离比j大
if (!st[j] && (t == -1 || dist[t] > dist[j]))
t = j;
}
// i!=0&&dist[t] == INF 当前距离最近的点到集合距离都是+∞,说明图是不连通的
if (i && dist[t] == INF) return INF;
// 只要不是第一个点,就把答案加上
if (i) res += dist[t]; // 需要先累加再更新,防止把自环的加进去了
// 2)用t更新其他点到集合的记录
for (int j