打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
算法分析第一次作业

1.问题

//有一个连通图
//图中有n个点,编号为1到n
//图中有m条无向边,格式为a b l 表示a与b之间有一条长为l的边
//没有重边,没有自环,所有点都相互连通
//求图的一棵最小生成树

2.解析

图的最小生成树即花费最少路径长度将图中所有点连通
有两种算法有优秀的时间复杂度解决这类问题
一种是prim算法,原理是基于贪心思想,先为空图取一个起始点,记录起点与其他点的距离,然后每步都是取图到其他未在图内的点的距离中最短的点和距离加入图中,然后用新加入的点更新图到其他点的距离,直到所有点加入其中,可得到最优解。
一种是kruskal算法,原理也是基于贪心思想,将所有边排序后优先取短的边,依次取下来直到所有点连通,可得到最优解。

3.设计

/*
10 30
1 2 5
1 3 7
1 5 6
1 6 20
1 8 12
1 9 23
1 10 13
2 4 15
2 5 17
2 6 8
2 7 19
2 9 37
3 5 21
3 4 33
3 8 29
3 7 12
3 10 18
4 7 16
4 10 39
4 6 26
5 7 26
5 9 7
5 10 17
6 7 18
6 8 24
6 10 16
7 8 22
7 10 19
8 9 7
9 10 28
*/
int head[110], ecnt = 0;
struct Edge {
	int from, to, nxt, val;
	Edge() {};
	Edge(int from, int to, int nxt, int val) : from(from), to(to), nxt(nxt), val(val) {};

	bool operator < (const Edge& a) const {
		return val < a.val;
	}

}edge[110 * 10];
inline void add(int u, int v, int w) {
	edge[++ecnt] = { u, v, head[u], w }; head[u] = ecnt;
	edge[++ecnt] = { v, u, head[v], w }; head[v] = ecnt;
}

bool vis[110];
int dis[110];
int prim(int s, int n) {
	printf("prim: start is %d\n", s);
	int sum = 0;
	memset(dis, 0x3f, sizeof dis);
	for (int i = head[s]; i; i = edge[i].nxt) {
		dis[edge[i].to] = edge[i].val;
	}
	vis[s] = 1;
	while (1) {
		int k = -1, minn = 0x3f3f3f3f;
		for (int i = 1; i <= n; ++i) {
			if (vis[i])	continue;
			if (minn > dis[i]) {
				minn = dis[i]; k = i;
			}
		}

		if (k == -1)	break;
		vis[k] = 1; sum += minn;
		printf("%d %d\n", k, minn);
		for (int i = head[k]; i; i = edge[i].nxt) {
			if (vis[edge[i].to])	continue;
			dis[edge[i].to] = min(dis[edge[i].to], edge[i].val);
		}
	}
	return sum;
}

int fa[110];
int find(int x) {
	if (fa[x] != x)	fa[x] = find(fa[x]);
	return fa[x];
}

void union_(int x, int y) {
	int fx = find(x), fy = find(y);
	if (fx != fy)	fa[x] = y;
}

int kruskal(int n) {
	puts("kruskal:");
	sort(edge, edge + ecnt);
	int cnt = 0, sum = 0;
	for (int i = 1; i <= n; ++i)	fa[i] = i;
	for (int i = 0; i < ecnt; ++i) {
		if (find(edge[i].to) != find(edge[i].from)) {
			printf("%d %d %d\n", edge[i].from, edge[i].to, edge[i].val);
			cnt++; sum += edge[i].val;
			union_(edge[i].to, edge[i].from);
		}

		if (cnt == n - 1)	break;
	}
	return sum;
}

int main() {
	int n, m; scanf("%d %d", &n, &m);
	for (int i = 0; i < m; ++i) {
		int u, v, w; 
		scanf("%d %d %d", &u, &v, &w);
		add(u, v, w);
	}
	
	printf("%d\n", prim(1, n));
	printf("%d\n", kruskal(n));
}

4.分析

prim算法
一开始初始化dis数组复杂度为O(n);
每次加点更新图的复杂度为两个O(n)复杂度的循环相加为O(2n);
总共要重复加n-1个点,所以总复杂度是T(n) = O(n - 1) * O(2n) + O(n) = O(n^2)

kruskal算法
对m条边排序复杂度为O(mlogm);
初始化并查集父亲数组复杂度为O(n);
接下来贪心加边的循环次数复杂度不稳定, 最优为O(n), 最差情况为O(m);
贪心加边循环内使用的是路径压缩并查集,其中find函数和union函数复杂度接近O(1);
所以T(n) = O(mlogm) + O(n) + O(m) = O(mlogm)
5.源码
https://github.com/wanshe2/Algorithm-course-assignment/blob/main/first course assignment

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
弱弱的模板啦啦啦
次小生成树
AGC052 B - Tree Edges XOR Editorial
spfa + slf优化
NOIP 2017提高组复赛解题报告
树链剖分详解及模板
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服