​ 网上关于最大团问题的回溯解法,大多为递归回溯,近日老师布置一个作业,采用迭代回溯的框架解决最大团问题(MCP),且要求多组解,有一定难度。

文章目录

  • 关于最大团
  • 回溯基本思想
  • 迭代基本框架`V1.0`
    • 约束函数`constraint()`
    • 限界函数`bound()`
    • 迭代函数`MCP()`
    • 试图得到多组解
  • 迭代基本框架`V2.0`(得多组解)
    • 迭代函数MCP()
    • 输出结果
    • 完整代码如下
    • 时间复杂度

关于最大团

​ 简单来说,团就是一个无向图G=<V,E>G=<V,E>G=<V,E>的一个子集,一个团内的顶点都与团内其他任意顶点有连接,而最大团其实就是顶点数目最多的团。

回溯基本思想

​ 显然,其解空间树为子集树,因此我们可以使用子集选取的思想,设当前拓展节点ZZZ位于解空间树的第iii层,在进入其左子树前,必须确认从顶点iii到已选入的顶点集中每一个顶点都有边相连;在进入右子树前,必须确认还有足够多的可选择顶点使得算法有可能在右子树中找到更大的团。

​ 由此我们得到回溯时的约束函数与限界函数:

​ 约束函数(constraint()):准备深入左子树时,判断当前节点是否可以加入团顶点集合;

​ 限界函数(bound()):准备深入右子树时,判断是否仍有可能产生最优解。

​ 在具体实现时,我们使用临界矩阵表示图GGG,用整型数组x[]来表示是否选择进入最大团。

迭代基本框架V1.0

约束函数constraint()

bool constraint(int k) {for(int j=1; j<k; j++) {if(x[j] && a[j][k]==0) {return false;}}return true;
}

​ 对于待加入节点kkk,如果存在某个已经在当前最大团集合的节点与其没有连接,那么就无法构成团,即返回false,否则返回true

限界函数bound()

bool bound(int k) {if(cn+n-k+1 > bestn) {return true;}return false;
}

​ 对于带考虑节点kkk,如果最大团集合内当前节点数cn加上剩下未考虑的节点数n-k+1的值严格优于当前最大团集合的节点最优值,则返回true,否则返回false

迭代函数MCP()

void MCP() {int k=1;while(1) {while(k<=n && constraint(k)) {    //顶点值合法且满足约束函数,选中,并深入左子树cn++;           //团内点的计数值++x[k++] = 1;     //选入}if(k>=n) {                     //对于图的一次遍历已经完成,记录结果for(int i=1;i<=n;i++)bextx[i] = x[i]; //拷贝解向量bestn = cn;                             //更新最优值} else {                     //否则,表示当前顶点不能被选入,需要考虑其是否满足限界函数x[k++]=0;                    }//如果满足限界函数,继续往后进入constraint的判断,但未选当前节点,表明是深入右子树while(!bound(k)) {             //如果不满足,则需要回溯k--; while(k && !x[k]) {            //回溯到上一个有效的选择k--;}if(k==0)return;             //如果是0,则退出cn--;                      //将其从最大团集合中取出x[k++]=0;                   //否则,深入其右子树}}
}

​ 这种迭代可以得出最大团的一组解,为什么说是一组解呢?我们可以看到在限界函数的定义种,当前团内节点数加上剩下的节点数与团的最优值相等时,是被剪掉了的,换句话说,不会去考虑相等的情况,因此只会有一组解。

试图得到多组解

bool bound(int k) {if(cn+n-k+1 >= bestn) {     //添加等于的情况return true;}return false;
}

​ 于是,我们试着在限界函数中添加等号,但如果是此种迭代框架,并不能达到输出多组解的目的,程序会产生死循环,为什么呢?我们以一个例子来说明:

这个无向图内有三个最大团,我们根据上述程序列出以下搜索时的数据表格:

k 是否添加 cn x[k] k bound
1 1 1 2 \
2 2 1 3 \
3 2 0 4 cn+n-k+1=2+5-4+1=4>bestn=0,故不回溯,深入右子树
4 2 0 5 cn+n-k+1=2+5-5+1=3>0,故不回溯,深入右子树
5 3 1 6 遍历完成,记录最优值{1,2,5},bestn=3
6 \ 3 \ \ 掉入判断bound,cn+n-k+1=3+5-6+1=3=bestn,故不回溯,深入右子树
6 \ 3 \ \ 直接略过约束函数的判断,调入记录最优值的语句块,重复记录,导致死循环

​ 如上图表格所示,其加入等号之后,会导致一直记录最优解,而造成死循环。故要想输出多组解不能直接更改限界函数。

上述代码其实是《计算机算法设计与分析习题解答》(第2版)王晓东著内的源码,大家有兴趣可以翻阅。

迭代基本框架V2.0(得多组解)

​ 我们需要解决记录解之后出现死循环的问题就需要在记录解之后,判断bound()之前,修改掉其k值,从而避免刚刚记录bestn就卡住了;一种可行的想法是:在记录了解之后,先提前找到最后一个加入团的顶点,将其回溯,即将其从团集合中剔除,然后才进入bound()的判断,这样既可以达到预期的效果。

迭代函数MCP()

​ 主要变化在:if(k>n)分支的处理上

void MCP() {int k=1;while(1) {while(k<=n && constraint(k)) {cn++;x[k++]=1;}if(k>=n) {                      //记录解if(cn>bestn) bestx.clear(); //一组更大的解,清空解向量集合if(cn>=bestn){               //相等顶点数的解(更大的解也会落入此)vector<int> chose;for(int i=1;i<=n;i++){   //得到哪些点被选入团if(x[i])chose.push_back(i);}bestx.push_back(chose);  //加入到解向量bestn = cn;                //更新最优值}k=n;                       //在判断bound前,提前回溯while(k && !x[k]) {          //找到最后一个加入的节点k--;}cn--;                     //移出团x[k++]=0;                   //标记不选} else {;x[k++]=0;}while(!bound(k)) {k--; while(k && !x[k]) {k--;}if(k==0)return;cn--;x[k++]=0;}}
}

输出结果

​ 如上图,可以输出多组解。根据输出的调试语句可以看到其回溯的顺序与我们预想的一致。

完整代码如下

#include <iostream>
#include <vector>
using namespace std;
#define MAX 1000
int n,m;
int a[MAX+1][MAX+1];
int cn=0,bestn=0;
int x[MAX+1];//bestx[MAX+1];
vector< vector<int> > bestx;
bool constraint(int k) {for(int j=1; j<k; j++) {if(x[j] && a[j][k]==0) {return false;}}return true;
}
bool bound(int k) {if(cn+n-k+1 >= bestn) {return true;}return false;
}
void output() {for(int i=0;i<bestx.size();i++){for(int j=0;j<bestx[i].size();j++){cout<<bestx[i][j]<<" ";}cout<<endl;}
}
void MCP() {int k=1;while(1) {while(k<=n && constraint(k)) {cn++;//cout<<"第"<<k<<"个满足约束条件cn="<<cn<<endl;x[k++]=1;}if(k>=n) {if(cn>bestn) bestx.clear();if(cn>=bestn){vector<int> chose;for(int i=1;i<=n;i++){if(x[i])chose.push_back(i);}cout<<endl;bestx.push_back(chose);bestn = cn;}k=n; while(k && !x[k]) {k--;}cn--;x[k++]=0;} else {//cout<<"第"<<k<<"个不满足约束条件"<<endl;x[k++]=0;}while(!bound(k)) {//cout<<"第"<<k<<"个节点不满足限界cn="<<cn<<endl;k--; while(k && !x[k]) {k--;}if(k==0)return;cn--;//cout<<"因为不满足约束也不满足限界而回溯至第"<<k<<"个节点cn="<<cn<<endl;x[k++]=0;}}
}
int main() {cout<<"请输入该图的顶点数V与边数E:"<<endl;cin>>n>>m;int u,v;   cout<<"请依次输入边的顶点u和v:"<<endl;for(int i=1; i<=m; i++) {cin>>u>>v;a[u][v]=1;a[v][u]=1;}MCP();    output();
}

​ 注释掉的为调试信息。

时间复杂度

​ 有nnn个顶点需要考虑,每个节点都都有深入左右子树两种选择,而解空间树高为nnn,故至多有2n2^n2n种可能情况,因此T(n)=n∗2nT(n)=n*2^nT(n)=n∗2n,故时间复杂度为:O(n2n)O(n2^n)O(n2n)。

迭代回溯的图最大团问题(可得到多组最优解)相关推荐

  1. 敏捷迭代燃尽图_3个敏捷燃尽报告以及如何使用它们

    敏捷迭代燃尽图 敏捷实践,对于那些刚起步且知识不足的人,有时可能会作为临时软件开发和项目管理方法而出现. 真相大相径庭. 敏捷软件的12条原则之一是: "最佳的体系结构,要求和设计来自自组织 ...

  2. 回溯法之递归回溯和迭代回溯

      回溯法有通用解题法之称,它可以系统的搜索一个问题的所有解或者任意解.它在问题的解空间树中,按深度优先策略从根节点出发搜索解空间树,算法搜索至解空间树的任意一个结点时,先判断该节点如(子树)是否包含 ...

  3. PMP考试常见图表讲解:迭代燃尽图、S曲线图……

    在PMP考试中,有很多图,其中在进度管理.成本管理中,尤其多.比较好理解的:甘特图.里程碑图.网络图,这里就不细讲了. 第一个是:迭代燃尽图(Iteration burn down chart) 燃尽 ...

  4. 华为Mate 40 Pro最新渲染图曝光:后置相机模组有变化

    根据华为官方日前官宣的消息,全新的华为Mate40系列旗舰将于10月22日20:00点正式亮相,不出意外的话将包含Mate40.Mate40 Pro.Mate40 Pro+和Mate40保时捷设计四款 ...

  5. 回溯、图论——最大团问题(求最大完全子图)

     1.问题分析 要想解决最大团问题,也就是求最大完全子图.我们需要了解相关概念,现在有如下图: (1)完全子图: 给定无向图G=(V,E),其中V是顶点集,E是边集.G'=(V',E')如果顶点集V' ...

  6. 回溯法----图的着色问题

    图的着色问题 1.问题描述 图的m-着色判定问题--给定无向连通图G和m种不同的颜色.用这些颜色为图G的各顶点着色,每个顶点着一种颜色,是否有一种着色法使G中任意相邻的2个顶点着不同颜色? 图的m-着 ...

  7. matlab费根鲍曼,matlab 数学实验 迭代 _ 蛛网图(免积分)

    数学实验-实验报告(免积分) 一.实验项目:Matlab实验三-迭代 二.实验目的和要求 a.熟悉MATLAB软件的用户环境,掌握其一般目的命令和MATLAB数组操作与 运算函数: b.掌握MATLA ...

  8. Bron–Kerbosch算法求一般图最大团/最大独立集

    最大团: V中取K个顶点,两点间相互连接   最大独立集: V中取K个顶点,两点间不连接    最大团数量 = 补图中最大独立集数 关于 Bron-Kerbosch算法(原文) 基础形式是一个递归回溯 ...

  9. LeetCode-题目详解(十一):回溯算法【递归回溯、迭代回溯】【DFS是一个劲往某一个方向搜索;回溯算法建立在DFS基础之上,在搜索过程中,达到结束/裁剪条件后,恢复状态,回溯上一层,再次搜索】

    这里写目录标题 一.概述 1.深度优先遍历(DFS) 和回溯算法区别 2. 何时使用回溯算法 3.回溯算法步骤 4.回溯问题的类型 二.LeetCode案例 39. 组合总和 40. 组合总和II 7 ...

  10. 回溯法-图的m着色问题

    本文参考https://blog.csdn.net/kevin_cyj/article/details/50385575 问题描述与思想概述 (1).问题的提出        给定无向连通图G和m种不 ...

最新文章

  1. 杨振宁眼中的物理学之美
  2. python循环报数游戏_python经典面试题之一:猴子报数
  3. 讨论IM软件企业知识—会谈session的概念,附连到IM软件层次图
  4. CSP认证201503-2 数字排序[C++题解]:哈希表、排序、结构体
  5. intimidated
  6. 第二十八期:Notepad++ 新 Logo 出炉,官网全新改版采用自适应设计
  7. oracle活跃用户,监控数据库中的活跃用户及其运行
  8. Hive分区表count(*)不起mapreduce的真相
  9. LeetCode 题 - 66. 列表元素加一 python实现
  10. 洛谷P1313 计算系数【快速幂+dp】
  11. php获取表单ip,PHP获取用户IP代码实现
  12. C# list删除 另外list里面的元素_[Python]列表(list)操作
  13. 《MFC游戏开发》笔记三 游戏贴图与透明特效的实现
  14. 服务器应用程序不可用解决方案集
  15. 小白Linux入门之:终端复用器Tmux使用参考
  16. Stimulsoft Reports.Net 2022.2.1
  17. Qt 出现空指针错误:The inferior stopped because it received a signal from the Operating System
  18. 2020年Java常问面试题--聂
  19. DFS 简单的剪枝和状态压缩 海贼王之伟大航路
  20. 秋色园Blog 博客系列索引

热门文章

  1. 北大编程网格【练习题】,北医计算概论课程编程题答案整理,C语言/python/C++
  2. win10系统,安装Photoshop_CS6并且无需序列号破解
  3. 自制Linux功能板
  4. Linux系列课程之一Linux的介绍
  5. 经营计划与经营利润分析动态报表的实现--业务需求
  6. Windows学习总结(12)——Windows 10系统开始运行-cmd命令大全
  7. 【生活随笔】研究生如何学习
  8. 数据分析篇 Microsoft Excel 2016阻止激活silverlight控件-03
  9. word文档通配符换行_PDF如何转化成Word文档?
  10. 行驶证识别技术-快速提取行驶证信息办理业务