最小生成树算法和二分图算法

最小生成树算法有二,其一prim算法,其二是克鲁斯卡尔算法,两者的时间复杂度分别为o(n^2)、o(mlogn)【n为点数,m为边数】,两个算法在不同的图中表现不同。

先来讲prim算法。

这个算法的核心思想就是把点分为两个集合A、B,A为已选点集合,B为未选集合。

经历n次循环,每次在未选集合中找一个里已选集合所构成的连通图最近的点放入已选集合中,并用这个点更新其他点;

思路和迪杰斯特拉算法很像,下面是代码和注释。

 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 510;

int g[N][N];//用邻接矩阵存图
int dist[N];//dist[i]表示i点到已选集合点构成的连通块的最短距离
int st[N];//st[i]=1表示i点为集合A,=0为集合B
int n, m;

int prim() {

	//初始化每个点到A集合的距离为无穷,出发点为点1,故dist[1]=0;
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;

	int res = 0;//res为集合A的权重

	for (int i = 0; i < n; i++) {

		int t = -1;

		for (int j = 1; j <= n; j++) {//在集合B中搜索可选点

			if (!st[j] && (t == -1 || dist[t] > dist[j]))
				t = j;

		}

		st[t] = 1;//标记该点进入集合A

		for (int j = 1; j <= n; j++) {//用该点更新其他点

			dist[j] = min(dist[j], g[t][j]);

		}
		res += dist[t];//更新权重

	}
	if (res >= 0x3f3f3f) return 0;
	else return res;

}


int main() {

	cin >> n >> m;

	memset(g, 0x3f, sizeof g);

	for (int i = 0; i < m; i++) {//接收数据

		int a, b, c; cin >> a >> b >> c;
		if (a != b)
			g[a][b] = g[b][a] = min(g[a][b], c);

	}

	int t = prim();
	if (!t) cout << "impossible";
	else cout << t;

}

然后是克鲁斯卡尔算法,这个算法用到了并查集的知识。

具体思路如下

先将所有边按权重大小排序,1~n依次递增,然后1~n遍历所有边,如果一条边的两个点不属于同一个集合,那么就将两个点所在的集合合并,并计算权重。

因为排序过,所以所得的结果是最小生成树

具体代码和注释如下;

 

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 200020;

struct edge {//用一个结构体存储所有边

	int a, b, w;

	bool operator< (const edge W)const {//重载<

		return w < W.w;

	}

}edges[N];

int n, m;
int p[N];

int find(int x) {//并查集核心代码,不晓得并查集的可以去翻我原先写过的并查集的介绍

	if (p[x] != x) p[x] = find(p[x]);
	return p[x];

}


int main() {

	cin >> n >> m;


	for (int i = 0; i < m; i++) {//接收所有边

		int a, b, c; scanf("%d%d%d", &a, &b, &c);

		edges[i].a = a, edges[i].b = b, edges[i].w = c;

	}

	sort(edges, edges + m);

	for (int i = 1; i <= n; i++) p[i] = i;

	int res = 0, cont = 0;//res为所有集合的权重和,cont为所有集合【不包括只含一个点的集合】的点数

	for (int i = 0; i < m; i++) {

		int a = edges[i].a, b = edges[i].b, w = edges[i].w;

		int ka = find(a), kb = find(b);//寻找这两个点的集合

		if (ka != kb) {

			res += w;
			p[ka] = kb;
			cont++;
		}

	}

	if (cont < n - 1) cout << "impossible";
	else cout << res;
}

然后是二分图的两个算法,一个是染色法,一个是匈牙利算法,前者是判断一个图是否为二分图,另一个算法的作用难以表述,我将用一道题解释。

先说染色法,其核心是dfs算法,思想如下;

在一个联通块中选一个点为起点,然后将其染色成黑色,并找到这个点的子节点,将其全部染色为白色,然后进入子点,寻找子点的所有子点,将其染为黑色,如此重复黑白两色的操作,如果不存在冲突的话则图为二分图,当且仅当图中存在一个边数为基数的环时存在冲突,即当找到一个子点时子点的颜色和父点的颜色相同;

具体代码如下;

 

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

const int N = 100010;

vector<vector<int>> ve(N);//用vector存储图

int n, m;

int col[N];//col[i]=1时i点为黑色,col[i]=2时i点为白色,col[i]=0时为无色;

bool dfs(int x, int c) {

	col[x] = c;

	for (int i = 0; i < ve[x].size(); i++) {//将子点全部染成与父点不同的颜色

		if (!col[ve[x][i]]) {

			if (!dfs(ve[x][i], 3 - c))
				return 0;


		}
		else if (col[ve[x][i]] == c) return 0;//如果子点有颜色,则判断是否与夫节点同色


	}

	return 1;
}

int main() {

	cin >> n >> m;

	for (int i = 1; i <= m; i++) {//接收图

		int a, b; scanf("%d%d", &a, &b);
		ve[a].push_back(b);
		ve[b].push_back(a);

	}

	int flag = 1;

	for (int i = 1; i <= n; i++) {//进行染色

		if (!col[i]) {

			if (!dfs(i, 1)) {//dfs=0时表明存在冲突,该图不为二分图
				flag = 0;
				break;
			}

		}

	}

	if (!flag) cout << "No";
	else cout << "Yes";

}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ACM 算法模板集 Contents 一. 常用函数与STL 二. 重要公式与定理 1. Fibonacci Number 2. Lucas Number 3. Catalan Number 4. Stirling Number(Second Kind) 5. Bell Number 6. Stirling's Approximation 7. Sum of Reciprocal Approximation 8. Young Tableau 9. 整数划分 10. 错排公式 11. 三角形内切圆半径公式 12. 三角形外接圆半径公式 13. 圆內接四边形面积公式 14. 基础数论公式 三. 大数模板,字符读入 四. 数论算法 1. Greatest Common Divisor最大公约数 2. Prime素数判断 3. Sieve Prime素数筛法 4. Module Inverse模逆元 5. Extended Euclid扩展欧几里德算法 6. Modular Linear Equation模线性方程(同余方程) 7. Chinese Remainder Theorem中国余数定理(互素于非互素) 8. Euler Function欧拉函数 9. Farey总数 9. Farey序列构造 10. Miller_Rabbin素数测试,Pollard_rho因式分解 五. 图论算法 1. 最小生成树(Kruscal算法) 2. 最小生成树(Prim算法) 3. 单源最短路径(Bellman-ford算法) 4. 单源最短路径(Dijkstra算法) 5. 全源最短路径(Folyd算法) 6. 拓扑排序 7. 网络预流和最大流 8. 网络最小费用最大流 9. 网络最大流(高度标号预流推进) 10. 最大团 11. 二分图最大匹配(匈牙利算法) 12. 带权二分图最优匹配(KM算法) 13. 强连通分量(Kosaraju算法) 14. 强连通分量(Gabow算法) 15. 无向图割边割点和双连通分量 16. 最小树形图O(N^3) 17. 最小树形图O(VE) 六. 几何算法 1. 几何模板 2. 球面上两点最短距离 3. 三点求圆心坐标 4. 三角形几个重要的点 七. 专题讨论 1. 树状数组 2. 字典树 3. 后缀树 4. 线段树 5. 并查集 6. 二叉堆 7. 逆序数(归并排序) 8. 树状DP 9. 欧拉路 10. 八数码 11. 高斯消元法 12. 字符串匹配(KMP算法) 13. 全排列,全组合 14. 二维线段树 15. 稳定婚姻匹配 16. 后缀数组 17. 左偏树 18. 标准RMQ-ST 19. 度限制最小生成树 20. 最优比率生成树(0/1分数规划) 21. 最小花费置换 22. 区间K大数 23. LCA - RMQ-ST 24. LCA – Tarjan 25. 指数型母函数 26. 指数型母函数(大数据) 27. 单词前缀树(字典树+KMP) 28. FFT(大数乘法) 29. 二分图网络最大流最小割 30. 混合图欧拉回路 31. 无源汇上下界网络流 32. 二分图最小点权覆盖 33. 带约束的轨道计数(Burnside引理) 34. 三分法求函数波峰 35. 单词计数,矩阵乘法 36. 字符串和数值hash 37. 滚动队列,前向星表示法 38. 最小点基,最小权点基

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值