用简单分支限界法解决最大团问题

        今天写算法题时遇到一个  最大团问题  我用了分支限界法求解,分享一下我的做法哈(^-^)

在图论中,完全图需要满足的条件是图中任意两顶点都是连通的,就像这样:

        通俗地说呢:最大团问题其实就是就是给你一个无向图,让你找出这个无向图中顶点数最多的完全图(图中顶点两两连通 )。

        本题使用的分支限界法类似于回溯法,但是回溯法一般用于在解空间中找到所有满足条件的解,而分支限界法则是用于在解空间中找出满足约束条件的最优解。因此一般需要使用优先队列来加快找出最优解的进程或是自己想一个限界函数来剪枝以提高算法的运行效率。

问题描述:一个无向图G中寒顶点个数最多的完全子图成为最大团。输入含n个顶点(编号为1~n),m条边的无向图,求其最大团的顶点个数。

输入描述:输入多个测试用例,每个测试用例的第一行包含两个正整数n,m,接下来m行,每行两个整数s,t 表示顶点 s 与顶点 t 之间有一条边,以输入n=0,m=0结束,规定1<=n<=50并且1<=m<=300

话不多说,代码如下:

#include<iostream>
#define MAXN 51
#define MAXE 301
 
using namespace std;

int n, m;//无向图中顶点个数与边的个数
int a[MAXN][MAXN];//无向图矩阵
int ans;//用于记录当前最优解
int cnt;//当前深度遍历时已经纳入最大团的顶点数目
int x[MAXN];//当前的解向量 (x[i]==1表示将编号为i的顶点放入解向量中)

//dfs函数设计:  i表示正在处理图中编号为i的顶点
void dfs(int i) {
	if (i > n) {//递归出口:  所有顶点都已处理完毕
		if (cnt > ans)//替换最优解
			ans = cnt;

		return;
	}
	bool flag = true;
	for (int j = 1; j < i; j++) {
		if (x[j] == 1 && a[i][j] == 0) {//判断当前顶点i与前 1,2,...,i-1个顶点的连通关系
			flag = false;
			break;
		}
	}
	if (flag) {//如果满足连通条件则将顶点放入解向量中
		x[i] = 1;
		cnt++;
		dfs(i + 1);
        //回溯
		x[i] = 0;
		cnt--;
	}
	if (cnt + n - i >=ans) {//利用一个简单的限界函数进行剪枝
    //只有当当前已经纳入结果的顶点数加上剩余未判断的顶点数大于等于当前最大解时才往下递归
		x[i] = 0;
		dfs(i + 1);
	}

}

int main() {

	int s, t;

	while (true) {
		cin >> n >> m;
		if (n == 0 && m == 0)  break;
		//初始化变量
		memset(a, 0, sizeof a);
		memset(x, 0, sizeof x);
		cnt = 0; ans = 0;


		for (int i = 1; i <= m; i++) {
			cin >> s >> t;
			a[s][t] = 1;
			a[t][s] = 1;
		}
		dfs(1);
		cout << "最大团中的顶点数量为 " << ans << endl;

	}

	return 0;
}

可以使用以下的测试数据:

5 8
1 2
1 3
1 4
1 5
2 4
3 4
3 5
4 5

0 0

最终的结果应该是4

能力有限,令请各位大神赐教ya。

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
最大团问题是指在一个无向图中找到一个最大的完全子图,其中任意两个节点都有边相连。采用分支限界法可以有效地解决最大团问题。 具体步骤如下: 1. 定义一个数据结构,记录当前搜索到的最大团大小、当前已选定的点集以及剩余可选点集。 2. 从剩余可选点集中选择一个点,将其加入当前已选定的点集中,并更新当前最大团大小。 3. 对于每个新加入的点,将其与已选定的点集中的所有点进行比较,找到与其相连的点,并将其从剩余可选点集中删除。 4. 递归搜索剩余可选点集中的点,重复执行步骤2和3,直到剩余可选点集为空。 5. 如果当前已选定的点集大小大于当前最大团大小,则更新最大团大小,并保存当前已选定点集。 6. 回溯到上一层,将已选择的点从当前已选定点集中删除,重新加入剩余可选点集中,继续搜索。 7. 最终得到的最大团即为所求。 以下是C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define MAXN 100 // 最大点数 int graph[MAXN][MAXN]; // 图的邻接矩阵 int n, max_clique; // 点数、最大团大小 bool is_clique[MAXN]; // 当前最大团的点集 void dfs(int cur_node, int cur_clique_size, int rest_nodes[], int rest_nodes_size) { if (rest_nodes_size == 0) { if (cur_clique_size > max_clique) { max_clique = cur_clique_size; for (int i = 0; i < n; i++) { is_clique[i] = false; } for (int i = 0; i < cur_clique_size; i++) { is_clique[rest_nodes[i]] = true; } } return; } if (cur_clique_size + rest_nodes_size <= max_clique) { // 剪枝 return; } int new_rest_nodes[MAXN], new_rest_nodes_size = 0; for (int i = 0; i < rest_nodes_size; i++) { int next_node = rest_nodes[i]; if (graph[cur_node][next_node]) { new_rest_nodes[new_rest_nodes_size++] = next_node; } } dfs(cur_node + 1, cur_clique_size, new_rest_nodes, new_rest_nodes_size); // 不选择当前节点 rest_nodes[cur_clique_size] = cur_node; dfs(cur_node + 1, cur_clique_size + 1, rest_nodes, cur_clique_size); // 选择当前节点 } int main() { scanf("%d", &n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { scanf("%d", &graph[i][j]); } } int rest_nodes[MAXN], rest_nodes_size = n; for (int i = 0; i < n; i++) { rest_nodes[i] = i; } max_clique = 1; dfs(0, 0, rest_nodes, rest_nodes_size); printf("Max clique size: %d\n", max_clique); printf("Nodes in the clique: "); for (int i = 0; i < n; i++) { if (is_clique[i]) { printf("%d ", i); } } printf("\n"); return 0; } ``` 该算法的时间复杂度为O(2^n),因为在最坏情况下需要搜索整个指数级的状态空间。但是,由于采用了剪枝策略,实际运行效率要高于暴力枚举。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值