最大团问题(迭代回溯法)

最大团问题(迭代回溯法)

问题

设计一个解最大团问题的迭代回溯法

理解

给定无向连通图 G = (V,E),其中 V 是非空集合,称为顶点集; E 是 V 中元素构成的无序二元组的集合,称为边集,无向图中的边均是顶点的无序对,无序对常用圆括号“()”表示,如果 UϵV ,且对任意两个顶点 u , vϵU 有( u,v) ϵE ,则称 U 是 G 的完全子图,G 的完全子图是 G 的团当前仅当 U 不包含在 G 的更大的完全子图中。 G 的最大团是指 G 中所含顶点数最多的团 。

思路

首先设最大团为一个空团,往其中加入一个顶点,然后依次考虑每个顶点,查看该顶点加入团之后仍然构成一个团,如果可以,考虑将该顶点加入团或者舍弃两种情况,如果不行,直接舍弃,然后递归判断下一顶点。对于无连接或者直接舍弃两种情况,在递归前,可采用剪枝策略来避免无效搜索。

为了判断当前顶点加入团之后是否仍是一个团,只需要考虑该顶点和团中顶点是否都有连接。

程序中采用了一个比较简单的剪枝策略,即如果剩余未考虑的顶点数加上团中顶点数不大于当前解的顶点数,可停止继续深度搜索,否则继续深度递归。

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

更多

猜你喜欢

转载自blog.csdn.net/qq_40649503/article/details/111713309
今日推荐