分支限界法之最小权顶点覆盖问题

问题描述

在这里插入图片描述

题目分析

我们先简明介绍一下题目,我们仍然需要将所有点分成U、V两个集合。什么叫做顶点覆盖呢?即为V 集合中的每个点,至少与一个U集合中的点直接相连。如图所示(红色点表示U集合中的点):
在这里插入图片描述
我们可以看到V集合中的顶点2、5、6,都与至少一个U集合中的顶点直接相连。反而如果按照下图分配则不满足条件:
在这里插入图片描述图中V集合的顶点2、5并没有U集合中的点与其直接相连,所以不是一种顶点覆盖。

那我们应该如何判断图是否被覆盖了呢?可以开辟一个数组c,如果c[j]==0,则表示U集合中没有任何一个顶点与其直接相连。
优先级
说完如何判断是否覆盖之后,我们来确定一下优先级。由于所有顶点都是带权的,我们的目的也是找到最小权覆盖,所以我们可以直接用权重作为优先级建立一个最小堆,从而实现优先队列。
界限函数
我们找的是最小点权,无法使用界限函数来对右孩子进行约束,因为如果当前结点不加入U集合中(即走右孩子路径),一定点权和更小,但是不一定会覆盖,所以不经过判断,我们也要将右孩子加入到队列中。
将活结点加入队列中
将活结点加入队列时,要对点的优先级、结果向量以及cover数组进行更新

代码

#include <iostream>
#include <queue>
using namespace std;
class HeapNode
{
    friend class VC;//求解最小权覆盖问题的类,融合了所有函数和所需的参数
    public:
        operator ()(int x,int y) const{return x < y;}//定义优先级
    private:
        int i,cn,*x,*c;//i表示结点序号,cn表示当前权重,x表示结果数组,c数组表示此时是否有一点i属于U,且i与j相连,如果有,则c[j]!=0
};
//解最小权顶点覆盖大类
class VC
{ 
    friend MinCover(int **,int [],int);
    private:
        void BBVC();
        bool cover(HeapNode E);//判断图是否已经被全部覆盖了
        void AddLiveNode(priority_queue<HeapNode> &H,HeapNode E,int cn,int i,bool ch);
        int **a,n,*w,*bestx,bestn;//邻接矩阵,节点数目,每个点的权重,结果向量,最优解
};
void VC::BBVC()
{
    priority_queue<HeapNode> H(100000);
    HeapNode E//扩展结点
    E.x = new int [n+1];//开辟结果向量
    E.c = new int [n+1];//开辟一数组,用于判断图是否被完全覆盖
    for(int j = 1;j <= n;j++)
    {
        E.x[j] = E.c[j] = 0;
    }
    int i = 1,cn = 0;//初始化当前点权总和为0
    while(true)
    {
        if(i > n)
        {
            if(cover(E))
            {
                for(int j = 1;j <= n;j++)
                    bestx[j]=E.x[j];
                bestn = cn;
                break;
            }
        }
        else
        {
            if(!cover(E))//如果当前没有完全覆盖,就将这个点加入到U集合中
                AddLiveNode(H,E,cn,i,1);
            AddLiveNode(H,E,cv,i,0);
        }
        if(H.size()==0)
            break;
        E = H.top();
        H.pop();
        cn = E.cn;
        i = E.i + 1;
    }
}
//判断图是否完全覆盖
bool VC::cover(HeapNode E)
{
    for(int j = 1;j <= n;j++)
    {
        if(E.x[j]==0 && E.c[j]==0)//如果此时j结点既不是U中的点,而且也没有U中的点与其相连,则至少这个点未被覆盖
        {
            return false;
        }
    }
    return true;
}
void VC::AddLiveNode(priority_queue<HeapNode> &H,HeapNode E,int cn,int i,bool ch)
{
    HeapNode N;//创建一个新的堆结点
    N.x = new int [n+1];
    N.c = new int [n+1];
    for(int j = 1;j <= n;j++)
    {
        N.x[j] = E.x[j];
        N.c[j] = E.c[j];
    }
    N.x[i] = ch;
    if(ch)
    {
        N.cn = cn + w[i];//此时i要加入集合U,所以其权重应该加上cn
        for(int j = 1;j <= n;j++)
        {
            if(a[i][j])
                N.c[j]++;//表明此时对于结点j来说,有一节点i属于U与其连接,表明这个点被覆盖了
        }
    }
    else
        N.cn = cn;
    N.i = i;
    H.push(N);
}
//MinCover完成最小覆盖计算
int MinCover(int **a,int v[],int n)//v表示的是结点权重数组
{
    VC Y;
    Y.w = new int [n+1];
    for(int j = 1;j <= n;j++)
        Y.w[j] = v[j];
    Y.a = a;
    Y.n = n;
    Y.bestx = v;
    Y.BBVC();
    return Y.bestn;
}
//主函数
int main()
{
    int n,e,u,v;//结点数,边数,u,v为结点编号
    cin>>n>>e;
    int a[n+1][n+1];
    for(int i = 0;i <= n;i++)
    {
        for(int j = 0;j <= n;j++)
        {
            a[i][j] = 0;//初始化为0
        }
    }
    p = new int [n+1];//定义结果向量
    for(int i = 1;i <= e;i++)
    {
        cin>>u>>v;
        a[u][v] = 1;
        a[v][u] = 1;
    }
    cout<<MinCover(a,p,n)<<endl;
    for(int i = 1;i<=n;i++)
        cout<<p[i]<<" ";
    cout<<endl;
    return 0; 
}

总结

我们可以看到,判断是否覆盖需要O(n)的时间复杂度,活结点入队需要O(n)的时间复杂度。所以在BBVC函数中,时间复杂度应该为O(n3)(**个人理解,未必正确:while循环的时间复杂度与节点数目有关,所以是n级别的,在if(!cover(E))中,判断的时间复杂度为O(n),内部的活结点入队操作时间复杂度为O(n),所以BBVC的时间复杂度为O(n3)**)。在主函数中,MinCover与BBVC的时间复杂度相同,我们还需要输入邻接矩阵的边,所以时间复杂度为O(n^3+e)。(个人理解,如果有不同想法欢迎指出,谢谢

  • 16
    点赞
  • 79
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值