A1142 Maximal Clique(25分)PAT 甲级(Advanced Level) Practice(C++)满分题解【图+极大团】

clique is a subset of vertices of an undirected graph such that every two distinct vertices in the clique are adjacent. A maximal clique is a clique that cannot be extended by including one more adjacent vertex. (Quoted from https://en.wikipedia.org/wiki/Clique_(graph_theory))

Now it is your job to judge if a given subset of vertices can form a maximal clique.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers Nv (≤ 200), the number of vertices in the graph, and Ne, the number of undirected edges. Then Ne lines follow, each gives a pair of vertices of an edge. The vertices are numbered from 1 to Nv.

After the graph, there is another positive integer M (≤ 100). Then M lines of query follow, each first gives a positive number K (≤ Nv), then followed by a sequence of K distinct vertices. All the numbers in a line are separated by a space.

Output Specification:

For each of the M queries, print in a line Yes if the given subset of vertices can form a maximal clique; or if it is a clique but not a maximal clique, print Not Maximal; or if it is not a clique at all, print Not a Clique.

Sample Input:

8 10
5 6
7 8
6 4
3 6
4 5
2 3
8 2
2 7
5 3
3 4
6
4 5 4 3 6
3 2 8 7
2 2 3
1 1
3 4 3 6
3 3 2 1

Sample Output:

Yes
Yes
Yes
Yes
Not Maximal
Not a Clique

题意分析:

这道题首先在于看懂题目,判断输入的图是否是一个两两连通的团,或者是否是极大团,最后总结归纳为以下两个条件:

  1. 是否两两相连
  2. 是否存在每个人都有的共同邻居

然后分别定义两个判断上述条件的bool类型函数,再根据要求在输出时进行判断 

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 210;
int Nv, Ne, v1, v2, m;
int edge[maxn][maxn] = { 0 };//邻接矩阵
bool isconnected(vector<int> vlist) {
	//判断是否两两相连
	bool flag = true;
	for (int i = 0; i < vlist.size()-1; i++) {
		for (int j = i + 1; j < vlist.size(); j++) {
			if (edge[vlist[i]][vlist[j]] != 1) {
				flag = false;
				return flag;
			}
		}
	}
	return flag;
}
bool addNeighbour(vector<int> vlist) {
	bool flag = false;//true可以加邻居
	for (int i = 1; i <= Nv; i++) {
		if (find(vlist.begin(), vlist.end(), i) == vlist.end()) {//该节点不在查询队列里
			for (int j = 0; j < vlist.size(); j++) {
				if (edge[vlist[j]][i] != 1) {
					flag = false;
					break;
				}
				else flag = true;
			}
		}
		if (flag)break;
	}
	return flag;
}
int main()
{
	cin >> Nv >> Ne;
	//输入
	for (int i = 0; i < Ne; i++) {
		cin >> v1 >> v2;
		edge[v1][v2] = edge[v2][v1] = 1;
	}
	//查询,判断是否是极大团
	//1、是否两两相连
	//2、是否存在每个人都有的共同邻居
	cin >> m;
	int num,v;
	for (int i = 0; i < m; i++) {
		cin >> num;
		vector<int> querylist(num);
		for (int j = 0; j < num; j++) {
			cin >> querylist[j];
		}
		sort(querylist.begin(), querylist.end());
		if (isconnected(querylist)) {
			if(!addNeighbour(querylist))cout << "Yes" << endl;
			else cout << "Not Maximal" << endl;;
		}
		else cout << "Not a Clique" << endl;
	}
	return 0;
}

运行结果如下:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 回溯法实现最大问题的伪代码: ``` function max_clique_backtrack(graph G, set V): max_clique = set() // 初始化最大为空集 if V is empty: // 如果 V 为空集,返回空集 return max_clique v = V.pop() // 从 V 中任选一个点 v candidates = V.intersection(G[v]) // 与 v 相邻的点作为候选点 for c in candidates: V_c = V.intersection(G[c]) // 与 c 相邻的点作为 V 的子集 clique = max_clique_backtrack(G, V_c.union({c})) // 递归求解子问题 clique.add(c) // 将 c 添加到最大中 if len(clique) > len(max_clique): // 如果找到了更大的最大,更新 max_clique max_clique = clique return max_clique ``` 支界法实现最大问题的伪代码: ``` function max_clique_BB(graph G, set V): max_clique = set() // 初始化最大为空集 queue = [(V, set())] // 初始队列,包含 (V, {}),表示还未选定的点集为 V,已选定的点集为 {} while len(queue) > 0: (V, clique) = queue.pop(0) // 取出队首元素 if len(clique) > len(max_clique): // 更新最大 max_clique = clique if len(V) == 0: // 如果 V 为空集,结束当前支 continue v = V.pop() // 从 V 中任选一个点 v candidates = V.intersection(G[v]) // 与 v 相邻的点作为候选点 for c in candidates: V_c = V.intersection(G[c]) // 与 c 相邻的点作为 V 的子集 queue.append((V_c, clique.union({c}))) // 将子问题入队 return max_clique ``` ### 回答2: 回溯法和支界法是解决最大问题的常用算法。最大问题的目标是在一个无向中找到一个最大的完全子,其中的每对节点之间都有边连接。以下是使用回溯法和支界法来解决最大问题的伪代码: 回溯法: 1. 初始化一个最大的变量max_clique,并将其置为空集。 2. 定义一个递归函数find_clique(G, R, P, X),其中: - G表示输入的无向 - R表示当前已选定的中的节点 - P表示可能的节点集合 - X表示已检查过的节点集合 3. 如果P和X都为空,则将R设为当前最大并返回。 4. 选择一个节点v使得P∪X中v的邻居数最多。 5. 遍历P中的每个节点u,并进行以下操作: - 如果u不是v的邻居,则将其从P中移除。 6. 遍历P中的每个节点u,并进行以下操作: - 将u加入中,将其从P和X中移除。 - 递归调用find_clique(G, R∪{u}, P∩N(u), X∩N(u))。 - 将u从中移除,并将其加入X。 7. 返回最大max_clique支界法: 1. 初始化一个最大的变量max_clique,并将其置为空集。 2. 初始化一个优先队列Q,并将空集作为第一个元素加入队列。 3. 当Q非空时,重复以下步骤: - 从Q中取出第一个元素(V)。 - 如果V的大小大于max_clique的大小,则更新max_clique为V。 - 选择V中一个节点v,遍历v的邻居节点u并进行以下操作: - 如果u不在V中,则将V∪{u}加入Q。 4. 返回最大max_clique。 以上是使用回溯法和支界法来解决最大问题的伪代码。这些算法的具体实现可以根据具体情况进行调整和优化,以提高算法的效率。 ### 回答3: 最大问题是图论中一个经典的问题,它的目标是在一个无向中找到一个具有最大顶点数的完全子,其中每两个顶点都有边相连。 最大问题可以使用回溯与支界法来实现。下面是一份用伪代码表示的实现: ``` function backtrack(graph, current_clique): if current_clique is a maximal clique: update maximum_clique if current_clique is larger return select a vertex v from graph for each vertex u in graph adjacent to v: if u is a valid candidate for adding to current_clique: add u to current_clique prune_invalid_vertices(graph, current_clique) backtrack(graph, current_clique) remove u from current_clique function prune_invalid_vertices(graph, current_clique): for each vertex v in graph: if v is valid but not a neighbor of any vertex in current_clique: remove v from graph function maximum_clique(graph): maximum_clique = an empty set current_clique = an empty set backtrack(graph, current_clique) return maximum_clique ``` 这段伪代码中的 `backtrack` 函数利用回溯的方式进行搜索,首先判断当前clique是否为最大clique,如果是则更新最大clique,并返回。然后从中选择一个顶点v,然后遍历与v相邻的顶点u,如果u是一个可以添加到当前clique中的有效候选顶点,则将其加入当前clique,然后进行剪枝操作,即删除那些不再是有效候选顶点的顶点。最后,对更新后的当前clique进行回溯,即递归调用backtrack函数,并在回溯结束后将u从当前clique中移除,以便尝试其他路径。 其中的 `prune_invalid_vertices` 函数用于剪枝操作,它遍历整个,删除那些在当前clique中没有邻居的有效但不再是候选顶点的顶点。 最大clique问题的求解主函数是 `maximum_clique`,它初始化最大clique和当前clique为空集合,并调用backtrack函数进行求解。最终返回最大clique。 上述伪代码是最大clique问题使用回溯与支界法实现的一个简单示例,它只展示了算法的框架,具体的实现细节还需根据具体情况进行调整和补充。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值