最大クリーク問題(反復バックトラッキング法)
問題
最大クリーク問題を解くための反復バックトラッキング法を設計する
理解
無向接続グラフG =(V、E)が与えられます。ここで、Vは頂点セットと呼ばれる空でないセットです。Eは、無向のエッジセットと呼ばれるVの要素で構成される順序付けられていない2つのタプルのセットです。グラフエッジはすべて順序付けられていない頂点のペアです。順序付けられていないペアは通常、括弧「()」で表されます。UϵVで、任意の2つの頂点uに対して、vϵUは(u、v)ϵEである場合、Uは完全なサブグラフと呼ばれます。 Gの、Gのホログラフは、UがGのより大きなホログラフに含まれていない場合にのみ、Gのクリークです。Gの最大のクリークは、Gの頂点の数が最も多いクリークを指します。
アイデア
まず、最大のグループを空のグループとして設定し、それに頂点を追加してから、各頂点を順番に検討し、グループに追加した後も頂点がグループを形成していることを確認します。可能であれば、頂点をに追加することを検討してください。グループ化または破棄します。そうでない場合は、直接破棄してから、次の頂点を再帰的に判断します。接続がないか直接放棄された2つのケースでは、再帰の前に、プルーニング戦略を使用して無効な検索を回避できます。
クリークを結合した後、現在の頂点がまだクリークであるかどうかを判断するには、頂点とクリーク内の頂点が接続されているかどうかを考慮するだけで済みます。
プログラムでは、比較的単純なプルーニング戦略が採用されています。つまり、残りの考慮されていない頂点の数とクリーク内の頂点の数が現在のソリューションの頂点の数以下の場合、詳細検索を停止できます。深い再帰は継続できます。
Cコードの実装
#include <iostream>
using namespace std;
class Clique
{
friend void MaxClique( int **, int *, int );
private :
void Backtrack( int i);
int **a; //图的邻接矩阵
int n; //图 的顶 点数
int *x; //当前解
int *bestx; //当前最优解
int cn; //当前顶点数
int bestn; //当前 最 大顶点数
};
void Clique::Backtrack( int i)
{
//计算最大团
if (i>n) //到达叶子节点
{
for ( int j=1;j<=n;j++)
bestx[j]=x[j];
bestn=cn;
cout<< "最大团:(" ;
for ( int i=1;i<n;i++)
cout<<bestx[i]<< "," ;
cout<<bestx[n]<< ")" <<endl;
return ;
}
//检 查 当前顶点是否与当前团连接
int ok=1;
for ( int j=1;j<i;j++)
if (x[j]&&a[i][j]==0) //i与j不连接,即j在团中,但是i,j不连接
{
ok=0;
break ;
}
if (ok) //进入左子树
{
x[i]=1;
cn++;
Backtrack(i+1); //回溯到下一层节点
x[i]=0;
cn--;
}
//通过上界函数判断是否减去右子树,上界函数用于确认还有足够多的可选择顶点使得算法有可能在右子树中找到更大的团
if (cn+n-i>=bestn)
{
//修改一下上界函数的条件,可以得到
x[i]=0; //相同点数时的解
Backtrack(i+1);
}
}
void MaxClique( int **a, int *v, int n)
{
//初始化 Y
Clique Y;
Y.x= new int [n+1];
Y.a=a;
Y.n=n;
Y.cn=0;
Y.bestn=0;
Y.bestx=v;
Y.Backtrack(1);
delete [] Y.x;
cout<< "最大团的顶点数:" <<Y.bestn<<endl;
}
int main()
{
int n;
cout<< "please input number of node:" ;
cin>>n;
//int a[n+1][n+1]; //由于定义的是int **a,且采用的是二维数组传参,因此
int **a= new int *[n+1]; //两种解决方法,一是给定第二维的大小,二是通过
for ( int k=0;k<=n;k++) //动态分配内存,这里采用了动态内存分配解决问题
a[k]= new int [n+1];
for ( int i=0;i<n+1;i++)
for ( int j=0;j<n+1;j++)
a[i][j]=0;
int edge;
cout<< "please input number of edge:" ;
cin>>edge;
cout<< "please input edge:" <<endl;
int v,w;
for ( int x=0;x<edge;x++)
{
cin>>v>>w;
a[v][w]=1;
a[w][v]=1;
}
int *p= new int [n+1];
MaxClique(a,p,n);
system( "pause" );
return 0;
}
テスト
入力図の場合:
出力:
参照
バックトラッキング実験(最大のグループ問題)https://wenku.baidu.com/view/fdb2875fe418964bcf84b9d528ea81c758f52e32.html
Cコードの実装2
#include <iostream>
using namespace std;
int m[101][101];//有向图的邻接矩阵
int x[101];//当前团的解
int bestx[101];//最优解
int n;//表示图的顶点数
int cn=0;//当前团的大小
int bestn;//当前最优值
void getbestn(int i)
{
if(i>n){
//递归出口,到根节点时,更新最优值和最优解,返回
bestn=cn;//更新最优值
for(int j=1;j<=n;j++)
bestx[j]=x[j];
return ;//返回
}
x[i]=1;//先假定x[i]=0;
for(int j=1;j<i;j++){
if(x[j]==1&&m[i][j]==0){
x[i]=0;//如果该点与已知解中的点无边相邻
break;//则不遍历左子树
}
}
if(x[i]==1){
//当且仅当x[i]==1时,遍历左子树
cn++;//该点加入当前解
getbestn(i+1);//递归调用
cn--;//还原当前解
}
x[i]=0;//假定x[i]==0
if(cn+n-i>bestn){
//如果当前值+右子树可能选择的节点<当前最优解,不遍历左子树
x[i]=0;
getbestn(i+1);
}
return ;
}
int main()
{
printf("输入图的顶点数:\n");
scanf("%d",&n);
//输入图的顶点数
//输入图的邻接矩阵
printf("输入图的邻接矩阵:\n");
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&m[i][j]);
//求最优解
getbestn(1);
//输出最优值
printf("最优值:%d\n",bestn);
//输出
for(int k=1;k<=n;k++)
if(bestx[k])
//输出最优解
printf("最优解:%-2d",k);
printf("\n");
return 0;
}
テスト
参照
バックトラッキング法で最大クリーク問題を解くhttps://blog.csdn.net/practical_sharp/article/details/102791951
もっと
- バックトラッキング方法-最大のグループ問題https://www.jianshu.com/p/99ed0f46b94c
- Javaでのアルゴリズムの実装-バックトラッキング方法-最大のグループ問題
https://blog.csdn.net/lican19911221/article/details/26228345 - アルゴリズムの設計と分析-最大のグループ問題(バックトラッキング方法)https://www.cnblogs.com/wkfvawl/p/11923848.html
- 第5章[バックトラッキング]最大のグループの問題とグラフのmカラーリングの問題https://blog.csdn.net/weinierbian/article/details/50379376