PAT第十章专题复习
-
图的定义
- 哈密顿图(PAT A 1122)
- 第一步:首尾结点相同?、输入个数是否合法?
- 第二步:除首尾结点外,每个顶点是否有且仅出现一次?
- 第三步:结点之间的边是否存在?
- 欧拉图(PAT A 1126)
- 首先统计结点度数,然后dfs判断是否是连通图。
- 如果是连通图且结点度数都是偶数,则为欧拉图;如果是连通图且仅有两个奇数度数的结点,则为半欧拉图;如果不是连通图或者结点度数不是以上两种情况,则不是欧拉图。
- 旅行商环路(PAT A 1150)
- 相邻结点是否可达?首尾结点是否相同?路径是否访问图中所有的点? 以上三个条件有一个不满足就不是旅行商环路
- 第二步:判断是否是最简环路。即结点个数是否最少
- 哈密顿图(PAT A 1122)
-
图的遍历
-
连通分量与强连通分量:
- 连通图是无向图中结点之间是否可达(可以直接可达或者间接可达)
- 连通分量是整个图总共有多少个连通分图
- 强连通图是有向图中结点之间是否可达(可以直接或者间接)
- 强连通分量是整个有向图有多少个强连通分图
-
DFS与BFS
-
dfs计算最高树的结点(PAT A 1021)
-
此题用到了一定的数学证明知识。要求树的最大根节点,只需要进行两次dfs。第一次,先任意选择一个结点,从这个结点开始遍历整棵树,获取能到达的最深顶点(记为集合A);第二次,从集合A中任意一个结点出发遍历整棵树,获取能达到的最深顶点(记为集合B)。然后集合A和集合B的并集就是所求最终根节点集合。
-
题目思路:
- ①第一遍dfs遍历计算连通分量并记录第一次遍历的最深的根节点,记为集合A,压入到最终答案的set类型变量ans中。
- ②第二遍dfs遍历先进行初始化,然后以A中任意结点为遍历初始结点,记录最深的根节点为集合B,将结果放入到ans中。
- 错误思路:一开始用的是bfs,总共遍历了n+1次,导致第三个测试点超时。
-
代码实现
#include<bits/stdc++.h> using namespace std; int n, maxDepth = 0; vector<int> G[10005]; int vis[10005] = {0}; vector<int> temp; set<int> ans; void dfs(int index, int depth){ vis[index] = 1; if(maxDepth < depth){ maxDepth = depth; temp.clear(); temp.push_back(index); }else if(maxDepth == depth){ temp.push_back(index); } for(int i = 0; i < G[index].size(); i++){ if(!vis[G[index][i]]){ dfs(G[index][i], depth+1); } } } int main(){ cin >> n; int t1, t2; for(int i = 0; i < n-1; i++){ cin >> t1 >> t2; G[t1].push_back(t2); G[t2].push_back(t1); } //第一遍dfs,计算连通分量,并记录第一次遍历的最深根节点 int num = 0, s1; for(int i = 1; i <= n; i++){ if(!vis[i]){ dfs(i, 1); if(i == 1){ s1 = temp[0];//记录集合A中的任意一个结点 for(int j = 0; j < temp.size(); j++){ ans.insert(temp[j]); } } num++; } } if(num != 1){ printf("Error: %d components",num); }else{ //第二遍dfs,记录新的最深结点集合,并将两个集合合并 //初始化 temp.clear(); memset(vis, 0, sizeof(vis)); maxDepth = 0; //第二次dfs dfs(s1, 1); for(int i = 0; i < temp.size(); i++){ ans.insert(temp[i]); } for(auto it = ans.begin(); it != ans.end(); it++){ printf("%d\n", *it); } } return 0; }
-
-
一个经验:遇到字符串的时候,不能直接将字符串作为下标,vector容器不好进行处理;应当将字符串与数字进行转换。
-
-
-
最短路径(PAT中题目都是Dijkstra求解)
-
fill数组使用
- 一维赋值:fill(d, d + maxn, 0x3f); 二维赋值fill(G[0], G[0] + maxn * maxn, 0x3f); 注意二维不能写成fill(G, G + maxn * maxn, 0x3f)的形式,会报错
-
注意点:在表示最大数值的inf时,不要用0x3f,有时不够大,可以用10^9来表示无限大。
-
A 1018 Public Bike Management (30 分)
-
【题目思路】
Dijkstra+DFS模板 + 第二尺度处理;
注意:
①模板当中for循环是否取到n需要注意题意。在本题中,总共有n+1个点,所以所有for循环上限都要取到n
-
【代码实现】
#include<bits/stdc++.h> using namespace std; const int maxn = 510; const int inf = 0x3fff; int G[maxn][maxn]; int vis[maxn] = {0}, d[maxn], wei[maxn]; vector<int> tempPath, path; vector<int> pre[maxn]; int c, n, sp, m; void dijkstra(int s){ //初始化操作 fill(d, d + maxn, inf); d[s] = 0; for(int i = 0; i <= n; i++){//需要注意下标是否可以取到n //寻找中介点index int index = -1, MIN = inf; for(int j = 0; j <= n; j++){ if(!vis[j] && MIN > d[j]){ MIN = d[j], index = j; } } //没找到 if(index == -1)return; //标记访问 vis[index] = 1; //优化d数组,得到pre for(int j = 0; j <= n; j++){//注意下标可以取到n if(!vis[j] && G[index][j] != inf){ if(d[j] > d[index] + G[index][j]){ d[j] = d[index] + G[index][j]; pre[j].clear(); pre[j].push_back(index); } else if(d[j] == d[index] + G[index][j]){ pre[j].push_back(index); } } } } } //dfs函数进行第二尺度处理 int minSend = inf, minBack = inf; void dfs(int ed){ //递归边界 if(ed == 0){ //计算第二尺度 int send = 0, back = 0; for(int i = tempPath.size() -1; i >=0 ; i--){ int index = tempPath[i]; if(wei[index] > c/2){ back += wei[index] - c/2; }else{ if(back - (c/2-wei[index]) < 0){ send += c/2 - wei[index] - back; back = 0; }else{ back -= c/2 - wei[index]; } } } if(send < minSend || send == minSend && back < minBack){ path = tempPath; minSend = send; minBack = back; } return; } //递归式 tempPath.push_back(ed); for(int i = 0; i < pre[ed].size(); i++){ dfs(pre[ed][i]); } tempPath.pop_back(); } int main(){ //初始化 fill(G[0], G[0] + maxn * maxn, inf); int si, sj; cin >> c >> n >> sp >> m; for(int i = 1; i <= n; i++){ cin >> wei[i]; } for(int i = 0; i < m; i++){ cin >> si >> sj; cin >> G[si][sj]; G[sj][si] = G[si][sj]; } dijkstra(0); dfs(sp); //输出 printf("%d 0", minSend); for(int i = path.size() - 1; i >= 0; i--){ printf("->%d", path[i]); } printf(" %d", minBack); return 0; }
-
-
A 1087 All Roads Lead to Rome (30 分)
-
【题目思路】
①图最短路径:Dijkstra+DFS求解。主思路没有问题
②细节处理之处需要注意
-
注意:
①在dfs函数中写标尺的时候,要注意Dijkstra已经将最短路径的第一标尺计算出来了。dfs中处理的是第二保持、第三标尺……
②写标尺时,第二标尺需要更新所有变量,第三标尺则不用更新第二标尺的变量,但是其余变量都需要更新。
//例如下面所示 //第二尺度处理 int hapi = 0; for(int i = tempPath.size() - 1; i >= 0; i--){ int now = tempPath[i]; hapi += wei[now]; } double avghapi = (1.0 * hapi / (tempPath.size() - 1)); //第二标尺更新所有变量 if(hapi > maxHapi){ maxHapi = hapi; maxavghapi = avghapi; path = tempPath; }else if(hapi == maxHapi && avghapi > maxavghapi){//第三标尺因为第二标尺的变量相同,所以不需要更新,但是剩下的变量都要更新。 maxavghapi = avghapi; path = tempPath; }
-
【代码实现】
#include<bits/stdc++.h> using namespace std; const int maxn = 210; const int inf = 1000000000; int n, k; map<string, int> city; string ci[210]; int G[maxn][maxn]; int vis[maxn] = {0}, d[maxn], wei[maxn]; vector<int> pre[maxn], path, tempPath; int maxHapi = 0, num = 0; double maxavghapi = 0; void dijkstra(int s){ //初始化操作 fill(d, d + maxn, inf); d[s] = 0; for(int i = 0; i < n; i++){ //寻找中介点index int index = -1, MIN = inf; for(int j = 0; j < n; j++){ if(!vis[j] && d[j] < MIN){ MIN = d[j], index = j; } } //没找到 if(index == -1)return; //标记访问 vis[index] = 1; //优化d数组,记录pre for(int j = 0; j < n; j++){ if(!vis[j] && G[index][j] != inf){ if(d[j] > d[index] + G[index][j]){ d[j] = d[index] + G[index][j]; pre[j].clear(); pre[j].push_back(index); }else if(d[j] == d[index] + G[index][j]){ pre[j].push_back(index); } } } } } //dfs函数处理第二尺度 void dfs(int v){ //递归边界 if(v == 0){ num++; //暂时先将起点压入tempPath中 tempPath.push_back(v); //第二尺度处理 int hapi = 0; for(int i = tempPath.size() - 1; i >= 0; i--){ int now = tempPath[i]; hapi += wei[now]; } double avghapi = (1.0 * hapi / (tempPath.size() - 1)); //注意第一尺度最短路径已经由Dijkstra函数计算出。 //注意写第二尺度条件时,不是像cmp函数中那样书写,第一个排序条件满足时,所有变量都要更新; //第二个排序条件满足时,第一个排序变量不用更新,其后所有变量都要更新,依次类推 if(hapi > maxHapi){ maxHapi = hapi; maxavghapi = avghapi; path = tempPath; }else if(hapi == maxHapi && avghapi > maxavghapi){ maxavghapi = avghapi; path = tempPath; } tempPath.pop_back(); return; } //递归函数 tempPath.push_back(v); for(int i = 0; i < pre[v].size(); i++){ dfs(pre[v][i]); } tempPath.pop_back(); } int main(){ //初始化图 fill(G[0], G[0] + maxn * maxn, inf); string st, temp, c1, c2; int hap, cost, ed; cin >> n >> k >> st; //起点初始化 city[st] = 0; ci[0] = st; wei[0] = 0; for(int i = 1; i <= n - 1; i++){ cin >> temp >> hap; city[temp] = i; ci[i] = temp; wei[i] = hap; if(temp == "ROM")ed = i; } //输入路线 for(int i = 1; i <= k; i++){ cin >> c1 >> c2 >> cost; G[city[c1]][city[c2]] = G[city[c2]][city[c1]] = cost; } dijkstra(0); dfs(ed); printf("%d %d %d %d\n", num, d[ed], maxHapi, (int)maxavghapi); for(int i = path.size() - 1; i >= 0; i--){ int index = path[i]; printf("%s", ci[index].c_str()); if(i != 0){ printf("->"); } } return 0; }
-
A 1111 Online Map (30 分)
-
【题目思路】
①使用两遍Dijkstra+DFS求出最短距离以及最短时间。
②本题主体思路简单,难点在于代码比较长,细节处理会出现错误,比如下面的注意点,在做题时就卡了比较长的时间。
注意点:
①邻接矩阵G[index] [j]中j为顶点编号,而邻接表G[index] [j]中j为下标,注意区分两者!!
-
-