分支限界法之最大团问题

A,最大团问题:给定一个无向图G=(V,E),其中V代表顶点集合,E代表边的集合。,如果U是V的子集,且对于U中任意两个顶点u和v都是相连的,即u-->v属于E,则称顶点子集U是图G的完全子图或者G的图。显然最大团就是指满足上述条件且含有顶点数最多的团。比如下图所示:
分支限界法之最大团问题

子集{A, B}是G 的大小为2 的团。这个团不是极大团(因为顶点数不是最多的),因为它包含在G 的更大团{A,B,E}中。{A, B, E}是G 的最大团。{A, D, E}和{B,C, E}也是G 的最大团。
B,分支限界法:该方法通常以广度优先或以最小耗费优先的方案搜索问题的解空间树。首先要弄明白什么是问题的解空间树。所谓解空间树就是问题的所有可能的解构成的树,因此问题的解一定在解空间树中。比如上面的图的解空间树如下图所示:
分支限界法之最大团问题

如上图所示,左边一列表示图G中的顶点,右边的树表示解空间树。每一层表示一个节点,树中的边1表示选择该节点,0表示不选择该节点。比如11001表示一个团,该团有3个顶点分别是1,2,5(从根节点出发对应上图);再比如01101表示一个含有3个顶点的团,它们分别是2,3,5。所以对于团问题,解空间树包含了所有团。求解最大团就是从解空间树种寻找顶点数最大的团。下面我们继续讨论分支限界法求解最大团问题。
当我们访问解空间树的一个顶点u时,假设cn表示u所在的团的顶点数,t表示顶点u在解空间树种的层次(比如上图1在层次为2,5层次为6),除此之外,每个顶点还有一个上界un表示最大团顶点数的上界,其中un=cn+n-t(n表示无向图中顶点总数)。在此优先队列式分枝限界算法中,un实际上也是优先队列(最大堆)中元素的优先级。算法总是从活结点优先队列中抽取具有最大un值的元素作为下一个扩展元素,因为un越大最后的找到的团顶点数可能就越多,这就是分支限界法中的限界思想。
C,算法思想:
1)首先假设解空间树已经生成了;
2)解空间树的根结点是初始扩展结点,对于这个特殊的扩展结点,其cn 值为0(表示当前团顶点数为0);
3)首先考察其左儿子结点。在左儿子结点处,将顶点u 加入到当前团中,并检查该顶点与当前团中其他顶
点之间是否有边相连。当顶点u与当前团中所有顶点之间都有边相连,则相应的左儿子结点是可行结点,将它加入到解空间树中并插入活结点优先队列,否则就不是可行结点;
4)接着继续考察当前扩展结点的右儿子结点。当un>bestn( bestn表示已经寻找到的团的顶点数,初始值为0)时,右子树中可能含有最优解,此时将右儿子结点加入到解空间树中并插入到活结点优先队列中;
5)继续上述3) 和 4)步骤直到搜索完整个解空间,算法结束;
6)搜索过程中通过上界un值来截枝避免访问不必要的节点。
下图为分支限界法解最大团问题的过程:

分支限界法之最大团问题
下面再补充一列,如下图所示:

分支限界法之最大团问题
下面来分析上图求解最大团中优先队列的变化情况:
1)从根节点0出发,将左子树1右子树2加入队列中并排序(根据un值排序,即图中节点的第二个数),队列为1,2;
2)1出队,将其左右子树3和4加入队列中并排序,队列为3,2,4;
3)3出队,其没有左子树不管,将右子树5加入队列并排序,队列为2,4,5;
4)2出队,将其左右子树6,7加入队列中并排序,队列为4,6,5,7(这里节点6和5的un值都为5为什么6排在5的前面呢,这时由于6在第二层,5在第三层,层次越低越排前面);
5)4出队,将其右子树8加入队列并排序,队列为6,5,7,8;
6)6出队,将其左右子树9,10加入队列并排序,队列为5,9,7,8,10(8,10没有孩子了,自然放在队列之后);
7)5出队,将其右子树加入队列并排序,队列为9,7,11,8,10(8和10依然排在最后);
8)9出队,将其右子树12加入对中并排序,队列为7,11,12,8,10;
9)7出队,将其右13,14加入对中并排序,队列为11,12,8,10,13,14(8,10,13,14均没有孩子了);
10)11出队,加入节点15,16,队列为12,8,10,13,15,14,16;
11)12出队,加入17,18,队列为8,10,13,14,15,17,16,18。此时队列中所有节点都没孩子节点了,然后依次出队算法结束。
这里的队列其实就是一个最大堆,我们知道在堆排序中,参照不同的属性(比如un的值)排序会有不同的堆序列,上面的分析是根据顶点的un值排序的堆,和图中稍有区别(因为上图可能还加入了其它参考值进行排序,因此对于un值相同的节点在队列中的顺序会有不同,我觉得这个无关紧要)。但是可以确定的是每次都是选择un值最大的节点出队(因为是大顶堆,其它元素un值不大于第一个)。



  • 44
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
最大团问题是图论中一个经典的问题,其目标是在一个无向图中找到一个包含最多顶点的团,即一个完全子图,其中任意两个顶点都有一条边相连。分支限界法是解决最大团问题的一种有效方法。 以下是使用C++实现最大团问题分支限界法的基本思路: 1. 定义一个结构体来存储图的信息,包括顶点数、邻接矩阵等。 2. 定义一个类来实现分支限界法,包括判断当前团是否为最大团的函数、计算当前团的下界、扩展当前团等。 3. 使用一个优先队列来存储当前团的扩展节点,每次从队列中取出下界最大的节点进行扩展。 4. 当队列为空时,返回最大团的结果。 下面是一个简单的C++代码实现: ```cpp #include <iostream> #include <cstring> #include <queue> using namespace std; const int MAXN = 100; struct Graph { int n; int adj[MAXN][MAXN]; }; class BranchAndBound { public: BranchAndBound(Graph& g) : graph(g) {} int maxClique() { int ans = 0; memset(visited, false, sizeof(visited)); memset(best, false, sizeof(best)); memset(cur, false, sizeof(cur)); curSize = 0; priority_queue<Node> q; q.push(Node(0, 0)); while (!q.empty()) { Node node = q.top(); q.pop(); if (node.lbound >= curSize) { int u = node.last + 1; while (u < graph.n) { if (canExtend(u)) { cur[u] = true; curSize++; Node nextNode(u, calcLowerBound(u)); q.push(nextNode); cur[u] = false; curSize--; } u++; } } else { ans = max(ans, curSize); for (int i = node.last + 1; i < graph.n; i++) { if (canExtend(i)) { cur[i] = true; curSize++; if (isMaxClique()) { memcpy(best, cur, sizeof(cur)); ans = curSize; } else { Node nextNode(i, calcLowerBound(i)); q.push(nextNode); } cur[i] = false; curSize--; } } } } return ans; } private: struct Node { int last; int lbound; Node(int l, int b) : last(l), lbound(b) {} bool operator<(const Node& other) const { return lbound < other.lbound; } }; Graph& graph; bool visited[MAXN]; bool best[MAXN]; bool cur[MAXN]; int curSize; bool canExtend(int u) { for (int i = 0; i < graph.n; i++) { if (cur[i] && !graph.adj[i][u]) { return false; } } return true; } int calcLowerBound(int u) { int cnt = 0; for (int i = u + 1; i < graph.n; i++) { if (canExtend(i)) { cnt++; } } return curSize + cnt; } bool isMaxClique() { for (int i = 0; i < graph.n; i++) { if (cur[i]) { for (int j = i + 1; j < graph.n; j++) { if (cur[j] && !graph.adj[i][j]) { return false; } } } } return true; } }; int main() { Graph g = { 5, {{0, 1, 1, 0, 1}, {1, 0, 1, 1, 0}, {1, 1, 0, 1, 1}, {0, 1, 1, 0, 1}, {1, 0, 1, 1, 0}} }; BranchAndBound bb(g); cout << bb.maxClique() << endl; return 0; } ``` 其中,Graph结构体用来存储图的信息,BranchAndBound类实现了分支限界法的主要函数,Node结构体用来存储优先队列中的节点信息。在main函数中,构造一个Graph对象,并用它初始化一个BranchAndBound对象,然后调用maxClique函数求解最大团问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值