zoukankan      html  css  js  c++  java
  • 回溯法——最大团问题(Maximum Clique Problem, MCP)

    概述:

      最大团问题(Maximum Clique Problem, MCP)是图论中一个经典的组合优化问题,也是一类NP完全问题。最大团问题又称为最大独立集问题(Maximum Independent Set Problem)。目前,求解MCP问题的算法主要分为两类:确定性算法和启发式算法。确定性算法有回溯法、分支限界法等,启发式算法、蚁群算法、顺序贪婪算法、DLS-MC算法和智能搜索算法等。

    问题描述:

      给定无向图G=(V,E),其中V是顶点集;E是V边集。如果U属于V,且对任意两个顶点u,v∈U有(u,v)∈E,则称U是G的完全子图。G的完全子图U是G的一个团当且仅当U不包含在G的更大的完全子图中。G的最大团是指G中所含顶点数最多的团。

      如果U属于V,且对任意u,v∈U有(u,v)不属于E,则称U是G的空子图。G的空子图U是G的独立集当且仅当U不包含在G的更大的空子图中。G的最大独立集是G中所含顶点数最多的独立集。
      对于任一无向图G=(V,E),其补图G'=(V',E')定义为:V'=V,且(u,v)∈E'当且仅当(u,v)∉E。
      如果U是G的完全子图,则它也是G'的空子图,反之亦然。因此,G的团与G'的独立集之间存在一一对应的关系。特殊地,U是G的最大团当且仅当U是G'的最大独立集。

    算法分析:

      无向图G的最大团和最大独立集问题都可以用回溯法在O(n2n)时间内解决。图G的最大团和最大独立集问题都可以看作是图G的顶点集V的子集选取问题。因此可用子集树表示问题的解空间。
      设当前扩展节点Z位于解空间树的第 层。在进入左子树前,必须确认从顶点 到已入选的顶点集中每一个顶点都有边相连。在进入右子树之前,必须确认还有足够多的可选择顶点使得算法有可能在右子树中找到更大的团。

    算法描述:

     1 #include <fstream>
     2 #include <iostream>
     3 #include <stdlib.h>
     4 #include <conio.h>
     5 using namespace std;
     6 
     7 #define MAX_v 50 //定义一个最大顶点个数
     8 typedef struct{
     9     int a[MAX_v][MAX_v]; //无向图G的邻接矩阵
    10     int v; //无向图G的顶点
    11     int e; //无向图G的边
    12     int x[50]; //顶点与当前团的连接,x[i]=1 表示有连接——即x[i]==1代表在当前最大团的解内
    13     int bestx[50]; //当前最优解
    14     int cnum; //当前团的顶点数目
    15     int bestn; //最大团的顶点数目
    16 }MCP;
    17 
    18 void Creat(MCP &G);
    19 void Backtrack(MCP &G,int i);
    20 
    21 void Creat(MCP &G){
    22     int i,j;
    23     ifstream fin("data.txt");
    24     if (!fin)
    25     {
    26         cout<<"不能打开文件:"<<"data.txt"<<endl;
    27         exit(1);
    28     }
    29     fin>>G.v;
    30     for (int i=1;i<=G.v;i++)
    31         for (int j=1;j<=G.v;j++)
    32             fin>>G.a[i][j];
    33     for(i=1;i<=G.v;i++) //初始化
    34     {
    35         G.bestx[i]=0;
    36         G.x[i]=0;
    37         G.bestn=0;
    38         G.cnum=0;
    39     }
    40     cout<<"———回溯法求解最大团问题———"<<endl;
    41     cout<<"输入初始化无向图矩阵为:"<<endl; //初始化
    42     for(i=1;i<=G.v;i++)
    43     {
    44         for(j=1;j<=G.v;j++)
    45             cout<<G.a[i][j]<<" ";
    46         cout<<endl;
    47     }
    48 }
    49 
    50 void Backtrack(MCP &G,int i){
    51     if (i>G.v){        //output()阶段
    52         for (int j=1; j<=G.v; j++)
    53             G.bestx[j] = G.x[j];    //记录最优解
    54         G.bestn =G.cnum;
    55         return ;
    56     }
    57     //检查顶点i与当前团的连接
    58     int OK = 1;
    59     for (int j=1; j<=i ; j++)
    60         if (G.x[j]&& G.a[i][j]==0){        //G.x[j]:顶点j在当前解的最大团内;G.a[i][j]:待考察i顶点与最大团中前i-1个顶点间边的关系
    61             //i不与j相连
    62             OK = 0;
    63             break;
    64         }
    65         if (OK) {        //进入左子树
    66             G.x[i] = 1;//把i加入团
    67             G.cnum++;
    68             Backtrack(G,i+1);
    69             G.x[i]=0;
    70             G.cnum-- ;
    71         }
    72         if (G.cnum+G.v- i>G.bestn){        //进入右子树——剪枝函数
    73             G.x[i] = 0;
    74             Backtrack(G,i+1);
    75         }
    76 }
    77 
    78 int main(){
    79     MCP G;
    80     Creat(G);
    81     Backtrack(G,1);
    82     cout<<"最大团包含的顶点数为:"<<G.bestn<<endl;
    83     cout<<"最大团方案为:( ";
    84     for (int i=1;i<=G.v;i++)
    85         if(G.bestx[i]==1){
    86             cout<<i<<" ";
    87         }
    88         cout<<")"<<endl;
    89         getch();
    90 }

     注:问题在于这种解法只能求得其中的一个最大团解!

    最大团问题百度百科:http://baike.baidu.com/view/7343867.htm 

  • 相关阅读:
    ConcurrentHashMap get方法保持同步的解释
    HashMap resize导致死循环
    ConcurrentHashMap原理详解
    单列索引和联合索引区别
    优先队列
    大根堆排序
    小根堆排序
    基础哈夫曼树-最简单的
    二叉查找树
    二叉查找树
  • 原文地址:https://www.cnblogs.com/xymqx/p/3745180.html
Copyright © 2011-2022 走看看