PAT第十章专题复习

PAT第十章专题复习

  • 图的定义

    • 哈密顿图(PAT A 1122)
      • 第一步:首尾结点相同?、输入个数是否合法?
      • 第二步:除首尾结点外,每个顶点是否有且仅出现一次?
      • 第三步:结点之间的边是否存在?
    • 欧拉图(PAT A 1126)
      • 首先统计结点度数,然后dfs判断是否是连通图。
      • 如果是连通图且结点度数都是偶数,则为欧拉图;如果是连通图且仅有两个奇数度数的结点,则为半欧拉图;如果不是连通图或者结点度数不是以上两种情况,则不是欧拉图。
    • 旅行商环路(PAT A 1150)
      • 相邻结点是否可达?首尾结点是否相同?路径是否访问图中所有的点? 以上三个条件有一个不满足就不是旅行商环路
      • 第二步:判断是否是最简环路。即结点个数是否最少
  • 图的遍历

    • 连通分量与强连通分量:

      • 连通图是无向图中结点之间是否可达(可以直接可达或者间接可达)
      • 连通分量是整个图总共有多少个连通分图
      • 强连通图是有向图中结点之间是否可达(可以直接或者间接)
      • 强连通分量是整个有向图有多少个强连通分图
    • 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为下标,注意区分两者!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码间烟火录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值