分支限界法最大团问题

【分支限界法】最大团问题

给定无向图G=(V, E),其中V是非空集合,称为顶点集;E是V中元素构成的无序二元组的集合,称为边集,无向图中的边均是顶点的无序对,无序对常用圆括号“( )”表示。如果U∈V,且对任意两个顶点u,v∈U有(u, v)∈E,则称U是G的完全子图(完全图G就是指图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={V, E},其中V={1,2,3,4,5},E={(1,2), (1,4), (1,5),(2,3), (2,5), (3,5), (4,5)}。根据最大团(MCP)定义,子集{1,2}是图G的一个大小为2的完全子图,但不是一个团,因为它包含于G的更大的完全子图{1,2,5}之中。{1,2,5}是G的一个最大团。{1,4,5}和{2,3,5}也是G的最大团。右侧图是无向图G的补图G’。根据最大独立集定义,{2,4}是G的一个空子图,同时也是G的一个最大独立集。虽然{1,2}也是G’的空子图,但它不是G’的独立集,因为它包含在G’的空子图{1,2,5}中。{1,2,5}是G’的最大独立集。{1,4,5}和{2,3,5}也是G’的最大独立集。
在这里插入图片描述
最大团问题的解空间树也是一棵子集树。子集树的根结点是初始扩展结点,对于这个特殊的扩展结点,其cliqueSize的值为0。 算法在扩展内部结点时,首先考察其左儿子结点。在左儿子结点处,将顶点i加入到当前团中,并检查该顶点与当前团中其它顶点之间是否有边相连。当顶点i与当前团中所有顶点之间都有边相连,则相应的左儿子结点是可行结点,将它加入到子集树中并插入活结点优先队列,否则就不是可行结点。
接着继续考察当前扩展结点的右儿子结点。当upperSize>bestn时,右子树中可能含有最优解,此时将右儿子结点加入到子集树中并插入到活结点优先队列中。算法的while循环的终止条件是遇到子集树中的一个叶结点(即n+1层结点)成为当前扩展结点。
对于子集树中的叶结点,有upperSize=cliqueSize。此时活结点优先队列中剩余结点的upperSize值均不超过当前扩展结点的upperSize值,从而进一步搜索不可能得到更大的团,此时算法已找到一个最优解。

// CliqueNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include<queue>
#include<vector>
using namespace std;


template<class Type>
class bbnode
{
    template<class Type>
    friend class Clique;
private:
    bbnode* parent;
    bool LChild;
};

template<class Type>
class CliqueNode
{
    template<class Type>
    friend class Clique;
public:
    bool operator < (CliqueNode<Type> &L);
    operator int() const { return un; }
private:
    int cn,//当前的节点数
        un,//
        level;//当前节点的层数
    bbnode<Type>* ptr;
};

template<class Type>
class Clique
{
    friend int main(void);
public:
    int BBMaxClique(int[]);
private:
    void AddLiveNode(priority_queue<CliqueNode<Type>, vector<CliqueNode<Type>>, less< CliqueNode<Type>>>&,
        int cn, int un, int level, bbnode<Type> E[], bool ch);
    int** a,
        n;
};

template<class Type>
int Clique<Type>::BBMaxClique(int bestx[])
{
    priority_queue< CliqueNode<int>, vector<CliqueNode<int>>, less< CliqueNode<int>>> priority;
    bbnode<Type>* E = nullptr;
    int i = 1,
        cn = 0,
        bestn = 0;
    while (i != n + 1)
    {
        bool OK = true;
        bbnode<Type>* B = E;
        for (int j = i - 1; j > 0;B=B->parent,j--)//找是不是一个团
        {
            if (B->LChild && a[i][j] == 0)
            {
                OK = false;
                break;
            }
        }
        if (OK)//
        {
            if (cn+1 > bestn)
                bestn = cn+1;
            AddLiveNode(priority, cn + 1, cn + n - i, i + 1, E, true);
        }
        if(cn+n-i>=bestn)
            AddLiveNode(priority, cn, cn + n - i, i + 1, E, false);

        CliqueNode<Type> N;//在取出来
        N = priority.top();
        priority.pop();
        E = N.ptr;
        cn = N.cn;
        i = N.level;  
    }
    for (int j = n; j > 0; j--)
    {
        bestx[j] = E->LChild;
        E = E->parent;
    }
    for (int j = 1; j <=n; j++)
    {
        cout << bestx[j] << "\t";
    }
    cout << endl;
    return bestn;
}

template<class Type>
void Clique<Type>::AddLiveNode(priority_queue < CliqueNode<Type>, vector<CliqueNode<Type>>, less< CliqueNode<Type>>>& H,
    int cn, int un, int level, bbnode<Type> E[], bool ch)
{
    
    bbnode<Type> *B=new bbnode<Type>;
    B->parent = E;
    B->LChild = ch;
    CliqueNode<Type> N;
    N.cn = cn;
    N.level = level;
    N.ptr = B;
    N.un = un;
    H.push(N);
}

template<class Type>
bool CliqueNode<Type>::operator<(CliqueNode<Type>& L)
{
    if (this->un < L.un)
        return true;
    return false;
}

int main()
{
    int** a;
    a = new int* [6];
    for (int i = 0; i < 6; i++)
        a[i] = new int[6];
    a[0][0] = 0, a[0][1] = 0, a[0][2] = 0, a[0][3] = 0, a[0][4] = 0, a[0][5] = 0;
    a[1][0] = 0, a[1][1] = 0, a[1][2] = 1, a[1][3] = 0, a[1][4] = 1, a[1][5] = 1;
    a[2][0] = 0, a[2][1] = 1, a[2][2] = 0, a[2][3] = 1, a[2][4] = 0, a[2][5] = 1;
    a[3][0] = 0, a[3][1] = 0, a[3][2] = 1, a[3][3] = 0, a[3][4] = 0, a[3][5] = 1;
    a[4][0] = 0, a[4][1] = 1, a[4][2] = 0, a[4][3] = 0, a[4][4] = 0, a[4][5] = 1;
    a[5][0] = 0, a[5][1] = 1, a[5][2] = 1, a[5][3] = 1, a[5][4] = 1, a[5][5] = 0;
    
    Clique<int> clique;
    clique.a = a;
    clique.n = 5;
    int bestx[6];
    clique.BBMaxClique(bestx);

    return 0;
}


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是最大团问题分支限界算法的伪代码: ``` maxClique(G): bestClique = [] clique = [] nodes = G.nodes() sortedNodes = sortNodesByDegree(nodes, G) expandCliques(clique, sortedNodes, G, bestClique) return bestClique expandCliques(clique, sortedNodes, G, bestClique): if no nodes left to add: if len(clique) > len(bestClique): bestClique = clique return if len(clique) + len(sortedNodes) <= len(bestClique): return node = sortedNodes.pop(0) expandCliques(clique + [node], getNextNodes(node, sortedNodes, G), G, bestClique) expandCliques(clique, sortedNodes, G, bestClique) getNextNodes(node, sortedNodes, G): return [n for n in sortedNodes if G.has_edge(node, n)] sortNodesByDegree(nodes, G): return sorted(nodes, key=lambda x: G.degree[x], reverse=True) ``` 其中,`maxClique` 函数接收一个无向图 `G` 作为输入,返回最大团。该函数首先初始化 `bestClique` 和 `clique` 两个空列表,分别代表当前最优解和当前搜索路径中的节点。然后,按照节点度数从大到小排序,调用 `expandCliques` 函数进行搜索,并返回 `bestClique`。 `expandCliques` 函数接收当前搜索路径中的节点列表 `clique`、按度数排序后的节点列表 `sortedNodes`、无向图 `G` 和当前最优解 `bestClique`。如果当前节点列表中已经没有可以添加的节点,则判断是否是最优解,并返回。如果添加当前节点后的团大小不足以超过当前最优解,则剪枝。否则,将当前节点添加到搜索路径中,更新 `sortedNodes` 列表,继续搜索。然后,回溯到上一层搜索。 `getNextNodes` 函数接收当前节点和按度数排序后的节点列表 `sortedNodes`、无向图 `G`,返回与当前节点相邻且尚未添加到搜索路径中的节点。 `sortNodesByDegree` 函数按照节点度数从大到小排序,并返回排序后的节点列表。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值