PAT甲级 图 相关题_C++题解

PAT (Advanced Level) Practice 用到图的存储方式,但没有用到图的算法的题目

目录

  • 1122 Hamiltonian Cycle (25)
  • 1126 Eulerian Path (25)
  • 1134 Vertex Cover (25)
  • 1142 Maximal Clique (25)
  • 1154 Vertex Coloring (25)

1122 Hamiltonian Cycle (25)

题目思路

  • n != queryV.size() 检查是否 query 覆盖了所有结点
  • kn != n + 1 检查 query 是否多走或少走
  • query[0] != query[kn-1] 检查是否成环
  • 遍历 query 检查是否每一步都可到达
#include<iostream>
#include<unordered_set>
using namespace std;
bool G[201][201] = {false};
int main()
{
	int n, m, u, v, k, kn;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++){
		scanf("%d%d", &u, &v);
		G[u][v] = G[v][u] = true;
	}
	scanf("%d", &k);
	for (int i = 0; i < k; i++){
		scanf("%d", &kn);
		int query[kn];
		unordered_set<int> queryV;
		for (int j = 0; j < kn; j++){
			scanf("%d", &query[j]);
			queryV.insert(query[j]);
		}
		bool isC = true;
		for (int j = 0; j < kn - 1; j++)
			if (!G[query[j]][query[j+1]]) isC = false;
		if (!isC || kn != n + 1 || n != queryV.size() || query[0] != query[kn-1]) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}

1126 Eulerian Path (25)

题目思路

  • 用邻接表存储图,可以用每个结点对应邻接点的个数(Adj[i].size())表示每个结点的度
  • 题干给出根据度奇偶性和个数判断的先决条件是要是连通图
  • 先用深搜判断连通,从任一结点开始,看可以遍历到多少结点
  • 若遍历到的结点数与结点总数不同,则不是连通图,直接输出 Non-Eulerian
  • 若相同则说明是连通图,再遍历邻接表输出结点度数并记录度数为奇数的结点个数
  • 根据奇度数结点个数输出是否为 Eulerian
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int> Adj[501];
bool vis[501] = {false};
int connected = 0;
void DFS(int root){
	connected++;
	vis[root] = true;
	for (int i = 0; i < Adj[root].size(); i++)
		if (!vis[Adj[root][i]]) DFS(Adj[root][i]);
}
int main()
{
	int n, m, u, v, oddnum = 0;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++){
		scanf("%d%d", &u, &v);
		Adj[u].push_back(v);
		Adj[v].push_back(u);
	}
	DFS(1);
	for (int i = 1; i < n + 1; i++){
		printf("%d%c", Adj[i].size(), i == n ? '\n' : ' ');
		if (Adj[i].size() % 2) oddnum++;
	}
	if (connected != n) printf("Non-Eulerian\n");
	else printf("%s\n", !oddnum ? "Eulerian" : oddnum == 2 ? "Semi-Eulerian" : "Non-Eulerian");
	return 0;
}

简化前代码

  • 用邻接矩阵存储图
  • 单独开数组记录结点度数
  • 用 BFS 判断是否连通
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int G[501][501] = {0};
bool vis[501] = {false};
int main()
{
	int n, m, u, v, connected = 0, oddnum = 0;
	scanf("%d%d", &n, &m);
	int degree[n+1] = {0};
	for (int i = 0; i < m; i++){
		scanf("%d%d", &u, &v);
		degree[u]++;
		degree[v]++;
		G[u][v] = G[v][u] = 1;
	}
	queue<int> q;
	q.push(n);
	vis[n] = true;
	while (!q.empty()){
		int now = q.front();
		q.pop();
		connected++;
		for (int i = 1; i < n + 1; i++){
			if (!vis[i] && G[now][i]){
				q.push(i);
				vis[i] = true;
			}
		}
	}
	for (int i = 1; i < n + 1; i++){
		printf("%d%c", degree[i], i == n ? '\n' : ' ');
		if (degree[i] % 2) oddnum++;
	}
	if (connected != n) printf("Non-Eulerian\n");
	else printf("%s\n", !oddnum ? "Eulerian" : oddnum == 2 ? "Semi-Eulerian" : "Non-Eulerian");
	return 0;
}

1134 Vertex Cover (25)

题目思路

  • vector<pair<int,int>> 保存边的端点
  • 对每个 query,新建一个 map 标记给出的 vertex
  • 遍历所有边,检查是否有边两个端点均不在给出的 vertex 中
  • 若有说明给出的 vertex 不能覆盖所有边,标记变量退出循环
  • 按标记变量输出要求内容
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;
int main()
{
	int n, m, k, nv, v;
	scanf("%d%d", &n, &m);
	vector<pair<int,int>> edges(m);
	for (int i = 0; i < m; i++) scanf("%d%d", &edges[i].first, &edges[i].second);
	scanf("%d", &k);
	for (int i = 0; i < k; i++){
		scanf("%d", &nv);
		unordered_map<int,bool> vertex;
		bool iscover = true;
		for (int j = 0; j < nv; j++){
			scanf("%d", &v);
			vertex[v] = true;
		}
		for (int j = 0; j < m; j++){
			if (!vertex[edges[j].first] && !vertex[edges[j].second]){
				iscover = false;
				break;
			}
		}
		printf("%s\n", iscover ? "Yes" : "No");
	}
	return 0;
}

1142 Maximal Clique (25)

题目思路

  • 输入无向边,用邻接矩阵将两个方向的边均存储起来
  • 每输入一个待检查的序列,就将标记变量 isclique & isMax 均设为 true,新建保存序列的数组和保存序列结点的集合
  • 输入待查序列同时将结点压入集合
  • 首先用二重循环检查序列是否两两相邻,若不是,说明现有序列非 clique,设置标记输出内容并跳出循环
  • 若通过上个检查,已知是 clique,要检查是否是最大的,也就是是否有其他结点与序列中每个点都相邻
  • 用一个变量按顺序遍历结点标号,取出不在序列中的结点,与序列结点两两配对检查是否相邻
    • 若有一对不相邻就从序列结点中 break 取下一个结点
    • 若能一直检查到序列结尾,发现此结点与序列每个结点均相邻,说明现有序列非 max clique,设置标记输出内容跳出循环
  • 最后检查变量按要求输出内容
#include<iostream>
#include<set>
using namespace std;
bool G[201][201] = {false};
int main()
{
	int nv, ne, u, v, m, K;
	scanf("%d%d", &nv, &ne);
	for (int i = 0; i < ne; i++){
		scanf("%d%d", &u, &v);
		G[u][v] = G[v][u] = true;
	}
	scanf("%d", &m);
	for (int i = 0; i < m; i++){
		bool isclique = true, isMax = true;
		scanf("%d", &K);
		int clique[K];
		set<int> cliqueV;
		for (int j = 0; j < K; j++){
			scanf("%d", &clique[j]);
			cliqueV.insert(clique[j]);
		}
		for (int j = 0; j < K - 1; j++){
			for (int k = j + 1; k < K; k++){
				if (!G[clique[j]][clique[k]]){
					isclique = false;
					printf("Not a Clique\n");
					break;
				}
			}
			if (!isclique) break;
		}
		if (isclique){
			for (int j = 1; j <= nv; j++){
				if (cliqueV.find(j) == cliqueV.end()){
					for (int k = 0; k < K; k++){
						if (!G[clique[k]][j]) break;
						if (k == K - 1) isMax = false;
					}
				}
				if (!isMax){
					printf("Not Maximal\n");
					break;
				}
			}
			if (isMax) printf("Yes\n");
		}
	}
	return 0;
}

1154 Vertex Coloring (25)

题目思路

  • vector<pair<int,int>>保存所有边
  • 将所有点的颜色存起来,同时放入set统计颜色个数
  • 枚举所有边,检查是否每条边的两点个颜色是否相同
  • 若有相同的边,设置标记
  • 根据标记 输出颜色个数 或 输出No
#include<iostream>
#include<vector>
#include<set>
using namespace std;
int main()
{
	int n, m, k, u, v;
	scanf("%d%d", &n, &m);
	vector<pair<int,int>> edges(m);
	for (int i = 0; i < m; i++)
		scanf("%d%d", &edges[i].first, &edges[i].second);
	scanf("%d", &k);
	for (int i = 0; i < k; i++){
		vector<int> colors(n);
		set<int> coloring;
		bool isokay = true;
		for (int j = 0; j < n; j++){
			scanf("%d", &colors[j]);
			coloring.insert(colors[j]);
		}
		for (int j = 0; j < m; j++)
			if (colors[edges[j].first] == colors[edges[j].second])
				isokay = false;
		if (!isokay) printf("No\n");
		else printf("%d-coloring\n", coloring.size());
	}
	return 0;
}
posted @ 2019-09-09 18:55  鲸90830  阅读(366)  评论(0编辑  收藏  举报