一.学习总结
1.图的思维导图
2.图学习体会
深度优先遍历与广度优先遍历
- 不同点:广度优先搜索,适用于所有情况下的搜索,但是深度优先搜索不一定能适用于所有情况下的搜索。因为由于一个有解的问题树可能含有无穷分枝,深度优先搜索如果误入无穷分枝(即深度无限),则不可能找到目标节点。所以,深度优先搜索策略是不完备的
- 广度优先搜索适用范围:在未知树深度情况下,用这种算法很保险和安全。在树体系相对小不庞大的时候,广度优先也会更好些。
- 深度优先搜索适用范围:刚才说了深度优先搜索又自己的缺陷,但是并不代表深度优先搜索没有自己的价值。在树深度已知情况下,并且树体系相当庞大时,深度优先搜索往往会比广度优先搜索优秀,因为比如8*8的马踏棋盘中,如果用广度搜索,必须要记录所有节点的信息,这个存储量一般电脑是达不到的。然而如果用深度优先搜索的时候却能在一个棋盘被判定出来后释放之前的节点内存。
拓扑排序算法
对一个有向无环图进行拓扑排序,是将图中所有顶点排成一个线性序列,满足弧尾在弧头之前。这样的线性序列称拓扑序列。其实说白了,拓扑排序就是一个广度优先搜索。
拓扑排序的方法如下:
(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它. (2)从网中删去该顶点,并且删去从该顶点发出的全部有向边. (3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止
Prim和Kruscal算法
Prim算法的实现过程:首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出最小生成树中各结点权重最小的边,并加到最小生成树中。(加入之后如果产生回路了就要跳过这条边,选择下一个结点。)当所有的结点都加入到最小生成树中后,就找出了这个连通图的最小生成树。
- Kruskal算法的实现过程:Kruskal算法在找最小生成树结点之前,需要对权重从小到大进行排序。将排序好的权重边依次加入到最小生成树中,(如果加入时产生回路就跳过这条边,加入下一条边)。当所有的结点都加入到最小生成树中后,就找到了这个连通图的最小生成树。
Prim算法和Kruskal算法都是从连通图中找出最小生成树的经典算法。从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排序后查找的。
所以说,Kruskal在算法效率上是比Prim快的,因为Kruskal只需一次对权重的排序就能找到最小生成树,而Prim算法需要多次对邻边排序才能找到。 Dijkstra算法
二、PTA实验作业
题目1:排座位
1、设计思路
int main{ 初始化并查集f[i] while(m--) { 输入宾客及关系 if(是朋友) 开始查找朋友 if (两宾客朋友相同) 记录朋友关系; } while(m--){ 输入两宾客 查找其朋友 如果有共同朋友,则返回的值相同; 是朋友,没有敌对关系,输出No; 不是朋友,也不敌对,输出OK; 有敌对,有共同的朋友,输出OK but... ; 只有敌对关系,输出No way } }
2、代码截图
3、PTA提交列表说明
4、调试问题
- 部分正确:DEV能运行出来,提交会有段错误,最后找到了是数组的空间不够。
题目2:六度空间
1、设计思路
int BFS(AdjGraph *G,int v)\{ count=1,层数level=0,最后一个节点last = v v入队列 while(队列不为空) 出队一个元素 while(p不为空) { temp=p->adjvex if(temp未被访问过) temp入队 访问标记visited[temp]=1 人数count++; 尾元素tail=temp } if(v==last) 层数level++ last=tail; if(lexel为6) break; endreturn count 返回节点人数cnt
2、代码截图
3、PTA提交列表说明
4、调试问题
- 答案错误:这道题是听过大佬讲后打出来的,犯了一个蠢错误:输出格式没有控制好空格
题目3:图着色问题
1、设计思路
int main(){ int mp[501][501]={0}; int color[501]; int v,e,k,x,y; 输入v,e,k为节点数、边数和颜色数 for(i=0 to i>x>>y; mp[x][y]=mp[y][x]=1; } map co; for(i=0 to i
2、代码截图
3、PTA提交列表说明
4、调试问题
- 答案错误:一开始的时候没有考虑图不连通的时候,导致答案错误,后来想用图遍历来判断图是否连通,过程太复杂,而且有些判断可能会重复
三、截图本周题目集的PTA最后排名
1、PTA排名
2、我的得分 280 2.5分
四.阅读代码:关键点
题目描述:
在一个无权图中,两个节点间的最短距离可以看成从一个节点出发到达另一个节点至少需要经过的边的个数。
同时,任意两个节点间的最短路径可能有多条,使得从一个节点出发可以有多条最短路径可以选择,并且沿着这些路径到达目标节点所经过的边的个数都是一样的。 但是在图中有那么一些特殊的节点,如果去除这些点,那么从某个初始节点到某个终止节点的最短路径长度要么变长,要么这两个节点变得不连通。这些点被称为最短路径上的关键点。 现给定一个无权图,以及起始节点和终止节点,要求输出该图上,这对节点间最短路径上的关键点数目。输入:
输入包含多组测试数据,每组测试数据第一行为4个整数n(1<=n<=10000),m(1<=m<=100000),s(1<=s<=n),t(1<=t<=n)。分别代表该图中的节点个数n,边数量m,起始节点s,终止节点t。
接下去m行描述边的信息,每行两个整数a,b(1<=a,b<=n 且 a != b)。表示节点a和节点b之间有一条边。输出:
对于每组测试数据,输出给定的这对节点间最短路径上的关键点数目。注意:若给定两个节点间不连通,则我们认为其关键点数目是0。
样例输入:
5 5 1 5
1 2 1 3 2 4 3 4 4 5 4 4 1 4 1 2 2 4 3 4 1 3样例输出:
1
0附上地址:
#include#include #include #include #include #include #define POINT_CNT 10020using namespace std;vector edge[POINT_CNT];int flag[POINT_CNT];int level[POINT_CNT];int n, m, s, t;int minStep;queue que;int main() { //freopen("input.txt", "r", stdin); //freopen("output.txt", "w", stdout); while (scanf("%d %d %d %d", &n, &m, &s, &t) != EOF) { for (int i = 0; i < POINT_CNT; i++) { edge[i].clear(); } while (m--) { int a, b; scanf("%d %d", &a, &b); edge[a].push_back(b); edge[b].push_back(a); } memset(flag, 0, sizeof(flag)); fill(level, level + POINT_CNT, POINT_CNT); while (!que.empty()) { que.pop(); } que.push(s); level[s] = 0; flag[s] = 1; while (!que.empty()) { int p = que.front(); que.pop(); int ps = edge[p].size(); int step = level[p]; if (p == t) { break; } for (int i = 0; i < ps; i++) { int to = edge[p][i]; if (flag[to] == 0) { que.push(to); level[to] = step + 1; flag[to] = 1; } } } while (!que.empty()) { que.pop(); } que.push(t); int ans = 0; memset(flag, 0, sizeof(flag)); flag[t] = 1; while (!que.empty()) { int p = que.front(); que.pop(); int ps = edge[p].size(); int step = level[p]; if (p == s) { break; } if (que.empty()) { ans++; } for (int i = 0; i < ps; i++) { int to = edge[p][i]; if (level[to] < step && flag[to]==0) { que.push(to); flag[to] = 1; } } } printf("%d\n", max(ans-1,0)); } return 0;}