今天写算法题时遇到一个 最大团问题 我用了分支限界法求解,分享一下我的做法哈(^-^)
在图论中,完全图需要满足的条件是图中任意两顶点都是连通的,就像这样:
通俗地说呢:最大团问题其实就是就是给你一个无向图,让你找出这个无向图中顶点数最多的完全图(图中顶点两两连通 )。
本题使用的分支限界法类似于回溯法,但是回溯法一般用于在解空间中找到所有满足条件的解,而分支限界法则是用于在解空间中找出满足约束条件的最优解。因此一般需要使用优先队列来加快找出最优解的进程或是自己想一个限界函数来剪枝以提高算法的运行效率。
问题描述:一个无向图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。