第9周总结博客(校历第11周)1-11周的总结,并查集拓扑排序做题总结,最小生成树和最短路总结

目录

一:1-11周的总结

二:优先队列并查集拓扑排序做题和复现赛总结

三:最小生成树和最短路总结

1:Dijkstra

2:Bellman-Ford

3:Floyd算法


一:1-11周的总结

1:纸上得来终觉浅,绝知此事要躬行。

参加程序设计竞赛这件事远远比自己想象的要难很多。就连最基础的STL,虽然看起来就那么几个操作,但真正拿去做题,写作业,就感觉要真正用好STL,需要花费非常大的功夫。

2:做一件事就要把一件事情做好,必须把经历集中于一件事情,否则将会一事无成。学校的活动固然是丰富多彩的,但是大学四年的时光也是短暂的,本身就是出身于一所普通的本科学校,就更要多为自己的未来谋划,不能沉迷于眼前的享乐,更不能像自己的同学一样觉得这所学校很多年以前能和山大相提并论,自恃高人一等,要认清现实,这就是一所普普通通的高校,打铁还需自身硬,否则将会在4年之后什么也不是。

3:今天的事情不能够拖到明天,自从学习ACM程序设计以来,我的作业和任务都是日日清,很少有拖到第二天的情况。

4:学习要讲究方法,追求效率。我发现我们专业考试不会考出了课件和课后题上的题型,但是很多同学买了很多课外的资料做,放着课件和课本上的提视而不见,结果第一学年第一学期成绩很不理想。我想学习算法也是如此,我也在探索能提高效率的方法,比如更多的笔算。

5:不要做井底之蛙。我发现我身边很多同学充满了优越感,经常痛斥XX学校垃圾,XX学院垃圾,觉得自己学院各种各种厉害。当看到自己不想看到的东西时,就给自己找理由,把他说成好的,当看到别人或者别的学院,学校比自己好,就找理由骂它垃圾。我想这对我来说,是应该吸取教训的。我认为在任何学校,任何学院,都有翱翔的雄鹰。我很羡慕41届ICPC亚洲区域赛获得金牌的山东理工大学队伍--Rising RP,也很崇拜大一大二就你会做莫比乌斯反演的其他院校的选手,并在像他们学习。

二:优先队列并查集拓扑排序做题和复现赛总结

1:通过做并查集的题目,和看并查集的题目,我感觉并查集简单的题目思路非常的相似,也很好做,种类并查集和带权并查集不是很好做,对于字符串类型的题来说,并查集可以和map相联系,用map存储字符串下标。并查集可以和贪心算法相结合,感觉各个题目就是合并的依据不同,很多题也是这个比较难思考。还有的题合并的时候需要同时维护其他数据,比如路径长度,集合中的元素数目等,有的题需要用逆向思维,删除改成增加,然后使用并查集合并求解,有的题先删除所有的节点,再重新增加,万变不离其宗,核心思想还是并查集。

2:拓扑排序我感觉比较的难做,很多题目我都不会做,可以说会做的没几个,感觉不是不会拓扑排序,而是不能理解题意,或者说是不能理解拓扑排序的依据,拓扑排序还需要多加练习。拓扑排序可以和DFS/BFS相结合,可以先排序再DFS,也可以同时进行,有些题存在负权问题,非常的棘手。

3:牛客网比赛,很多题看着好做,实际就做不出来,也暴露了自己优先队列学的不怎么样,并查集部分的题感觉还可以。甚至后面几道题,明明2周前做过,再写却还是很卡手,写不出来。通过这次比赛明白了,STL必须熟练才行。

单调队列保证队列中各个元素大小单调递减(即,最大元素在对头,最小的在队尾),同时每个元素的下标单调递增(按校标递增的顺序添加这点可以不用考虑)。这样便保证队首元素最大,而且更新的时候队首永远是当前最大。因此,这个队列需要在两头都可以进行删除,在队尾插入。 维护方法:在每次插入的时候,先判断队尾元素,如果不比待插入元素大就删除,不断删除队尾直到队尾元素大于待插入元素或者队空。删除的时候,判断队首,如 果队首元素下标小于当前段左边界就删除,不断删除队首直到队首元素下标大于等于当前段左边界(注意:这时队列肯定不为空),队首元素就是当前段的最优解。

三:最小生成树和最短路总结

1:最小生成树:
Kruskal算法更适合找稀疏图的最小生成树
代码实现:
#include 
using namespace std;
#define N 100
struct edge {
	int u, v, value;
}myedge[N * N];
int n, m, p[N];
bool cmp(const edge& p1, const edge& p2) {
	return p1.value < p2.value;
}
int find(int x) {
	return (p[x] == x) ? x : (p[x] = find(p[x]));
}
int main() {
	while (scanf("%d%d", &n, &m) == 2) {
		char a[2], b[2];
		int i, x, y, sum = 0, value, counter = 0;
		for (i = 0; i < m; i++) {
			scanf("%s%s%d", a, b, &myedge[i].value);
				myedge[i].u = a[0] - 'a';
			myedge[i].v = b[0] - 'a';
		}
		for (i = 0; i < n; i++) p[i] = i;
		sort(myedge, myedge + m, cmp);
		for (i = 0; i < m; i++) {
			value = myedge[i].value;
			x = find(myedge[i].u);
			y = find(myedge[i].v);
			if (x != y) {
				sum += value;
				p[y] = x;
				counter++;
				if (counter >= n - 1) break;
			}
		}
		printf("%d\n", sum);
	}
	return 0;
}
相对于 Kruskal 算法 , Prim 算法也适用于稠密图
2: 最短路
主要算法有 : Dijkstra Bellman-Ford SPFA Floyd. 前两个是单源最短路径, 旨在找某个点到其他所有点的最 短路. Dijkstra 适合不含负权的图 , SPFA 可以解决有负权 边的图 ( 但不能含负权回路 ). Floyd 算法可以找出任意两点间的最短路 .

1:Dijkstra

Dijkstra时间复杂度: O(|V|*|V|). 如是用优先队列维护最小值, 可以 做到O(|V|*log|E|).  但是这个算法 不能有负权边

2:Bellman-Ford

当负权存在时 , 最短路都不一定存在 ( 有负环一定不存在 )
这时需要Bellman-Ford 算法
for(int i = 0; i < n; i++) dis[i] = INF;
dis[0] = 0;
for(int k = 0; k < n-1; k++) { //控制次数
for(int i = 0; i < m; i++) {
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
} }

每次仅对最短路程发生了变化的结点的相邻边执行操作, 效率可能会高一些.可以使用队列来维护这些点。

最坏时间复杂度 : O(|V|*|E|). 但一般没那么大 . 为了避免最坏 情况的出现 , 在正权图上应使用效率更高的 Dijkstra 算法.当一个结点进入队列的次数超过 |V| , 则一定存在负环 .   在稠密图中复杂度比迪杰斯特拉算法高.

3:Floyd算法

如果需要求任意两点之间的距离,不必调用nDijstraBellman-ford算法,可以使用Floyd-Warshall算法。Floyd算法利用了动态规划,用d[i][j][k]表示从i到j,经过编号不超过k的点所得到的最短距离,则

d[i][j][k] = min{d[i][j][k-1], d[i][k][k-1] + d[k][j][k-1]}

你可能感兴趣的:(图论)