分支限界——最大團問題
阿新 • • 發佈:2020-12-12
技術標籤:演算法
給定有一個無向圖,找出最大團個數。
最大團也就是該圖中最大的完全圖(各頂點之間都有邊)。
演算法思想:設p為所有點集的集合,依次取出p中的頂點作為團的起始點,也就是以該點為起點開始拓展,每次檢視相鄰頂點是否與團內各點聯通,若true,則加入該點。
為了進一步降低時間複雜度,採取記憶化遞迴的方式,從後向前遍歷頂點,用cnt陣列記錄該點以後所成最大團的個數,以此知該點的潛力,達到剪枝的效果。
#include<bits/stdc++.h>
using namespace std;
#define maxm 55
int g[maxm][maxm];//存圖
int vis[maxm];//存放已選擇的點
int cnt[maxm];//cnt[i]表示用編號>=i的點能組成的最大團的點數
int ans, n;
bool dfs(int cur, int num) {//從第cur個點開始向後新增,當前點是第num個
for (int i = cur + 1; i <= n; i++) {
if (cnt[i] + num <= ans)//當前點後的所有點組成的最大團的最大點數+已經加入的點數<=當前最佳答案(最好的情況都不可能超過當前最優解,則進行剪枝)
return 0;
if (g[cur][i])//兩點相鄰
{
int ok = 1;
for (int j = 0; j < num; j++)//是否和當前已經加入團的所有點相鄰
if (!g[i][vis[j]])
{
ok = 0;
break;
}
if (ok) {//當前點可以加入團中
vis[num] = i;
if (dfs(i, num + 1))//第一次dfs成功的一定是最大的
return 1;
}
}
}
ans = max(ans, num);
return (ans == max(num, ans) ? 0 : 1);
}
void maxclique () {
for (int i = n; i > 0; i--)
{
vis[0] = i;
dfs(i, 1);
cnt[i] = ans;
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
cin >> g[i][j];
g[j][i] = g[i][j];
}
maxclique();
cout << cnt[1];
return 0;
}
/*
5
1 1 0 1 1
1 1 1 0 1
0 1 1 0 1
1 0 0 1 1
1 1 1 1 1
*/