最小权顶点覆盖问题

问题描述:

给定一个赋权无向图G=(V,E),每个顶点vV都有一个权值w(v)。如果UV,且对任意(u,v)∈EuUvU,就称U为图G的一个顶点覆盖。G的最小权顶点覆盖是指G中所含顶点权之和最小的顶点覆盖。

问题解决:

用优先队列分支限界方法解最小权顶点覆盖,在算法的搜索的进程中保存当前已构造出的部分解空间树,在算法搜索达到叶节点时,其最优值对应的最优解同时保存下来。优先队列的优先级为每个活节点的权值,权值最小的活节点成为下一个扩展节点。

<1>算法思路:

算法BBVC求取最小权顶点覆盖的最优值和最优解;算法cover判断图中顶点是否被完全覆盖;算法AddLiveNode将产生的子节点加入到活节点优先队列的最小堆中;算法MinCover返回最小权顶点覆盖的最优值和最优解。

<2>、算法程序:

#include<iostream>
#include<fstream>
#include"MinHeap.h"
using namespace std;

//最小堆结点
class HeapNode                              //堆结点类;
{
    friend class DealNode;
public:
    operator int()const{return cn;}     
private:
    int i,                                  //i标示堆中结点号
        cn,                                 //cn标示当前加入的覆盖顶点中权重之和
        *x,                                 //x数组标示那些顶点加入了覆盖顶点的行列
        *c;                                 //c数组标示X中的覆盖顶点中所有的邻接顶点
};

// VC类用来对堆中结点内部的的操作
class DealNode
{
    friend MinCover(int **,int [],int);                          
private:
    void BBVC();
    bool cover(HeapNode E);
    void AddLiveNode(MinHeap<HeapNode>&H,HeapNode E,int cn,int i,bool ch);
    int **a,n,*w,*bestx,bestn;
};

void DealNode::BBVC()
{
    //建立初始空堆
    MinHeap<HeapNode>H(1000);   
    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;  
    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))
                AddLiveNode(H,E,cn,i,true);//加入结点标号为i 的结点到顶点覆盖集中,并把更新后的结点再插入堆中
            AddLiveNode(H,E,cn,i,false); //不把结点标号为 i 的结点加入到顶点覆盖集中,并把更新后的结点插入堆中
        }
        if(H.IsEmpty())break;
        H.RemoveMin(E);  //取堆顶点赋给E
        cn=E.cn;
        i=E.i+1;                                                        
    }
}

//检测图是否被覆盖
bool DealNode::cover(HeapNode E)
{
    for(int j=1;j<=n;j++)
    {
        if(E.x[j]==0 && E.c[j]==0)//存在任意一条边的两个顶点都为0的情况下,为未覆盖情况
            return false;      //X[j]记录覆盖顶点,c[j]记录与覆盖顶点相连的顶点 0表征未覆盖,1表征已覆盖
    }                       
    return true;
}

void DealNode::AddLiveNode(MinHeap<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 ? 1:0;

    if(ch)
    {
        N.cn=cn+w[i];       //记录i顶点是否加入覆盖的行列中;
        for(int j=1;j<=n;j++)
            if(a[i][j]>0)   //如果i,j相邻,刚把j顶点加入覆盖邻接顶点集中;
                N.c[j]++;                                                          
    }
    else 
    {
        N.cn=cn;
    }
    N.i=i;
    H.Insert(N);                                                        //插入堆中
}

int MinCover(int **a,int v[],int n)
{
    DealNode Y;
    Y.w=new int[n+1];
    for(int j=1;j<=n;j++)
    {
        Y.w[j]=v[j];                   //初始化DealNode类对象Y;
    }
    Y.a=a; 
    Y.n=n;
    Y.bestx=v;                        //将地址赋予bestx,
    Y.BBVC();
    return Y.bestn;                   //bestn是最后的最小顶点覆盖集权重;
}  

int main()
{
    int startV,endV;        //一条边的起始节点,终止节点
    int vertexNum,edgeNum;  //顶点数,边数

    //读出输入文件中的数据
    fstream fin;
    fin.open("input.txt",ios::in);
    if(fin.fail())
    {
        cout<<"File does not exist!"<<endl;
        cout<<"Exit program"<<endl;
        return 0;
    }

    fin>>vertexNum>>edgeNum;

    int **a;                            //图的邻接矩阵表示,1表示有边
    a=new int *[vertexNum+1];

    for(int k=0;k<=vertexNum;k++)
        a[k]=new int[vertexNum+1];
    for(int i=0;i<=vertexNum;i++)
        for(int j=0;j<=vertexNum;j++)
            a[i][i]=0;

    int *p;                             //顶点的权值数组
    p=new int[vertexNum+1];
    for(i=1;i<=vertexNum;i++)
        fin>>p[i];

    for(i=1;i<=edgeNum;i++)
    {
        fin>>startV>>endV;
        a[startV][endV]=1;
        a[endV][startV]=1;
    }

    //将结果数据写入到输出文件
    fstream fout;
    fout.open("output.txt",ios::out);

    int minVertex=MinCover(a,p,vertexNum);

    cout<<minVertex<<endl;
    fout<<minVertex<<endl;

    for(i=1;i<=vertexNum;i++)
    {
        cout<<p[i]<<" ";
        fout<<p[i]<<" ";
    }

    cout<<endl;

    fin.close();
    fout.close();
    system("pause");
    return 0;
}
    2387 

其中最小堆代码MinHeap.h

template <class Type>
class MinHeap                           //最小堆类;
{
public:                                 
    MinHeap(Type a[], int n);           //带两参数的构造函数,在此程序中没有应用;
    MinHeap(int ms);                    //构造函数重载,只初始化堆的大小,对堆中结点不初始化;另外,堆元素的存储是以数组
    ~MinHeap();                         //形式,且无父、子指针,访问父亲结点,利用数组标号进行;
    bool Insert(const Type &x);         //插入堆中一个元素;
    bool RemoveMin(Type &x);            //删除堆顶最小结点;
    void MakeEmpty();                   //使堆为空
    bool IsEmpty();
    bool IsFull();
    int Size();
protected:
    void FilterDown(const int start, const int endOfHeap);            //自顶向下构造堆
    void FilterUp(const int start);                                   //自底向上构造堆
private:
    Type *heap;                                                                 
    int maxSize;
    const int defaultSize;
    int currentSize;                                                  //堆当前结点个数大小
};

template <class Type>
MinHeap<Type>::MinHeap(int ms):defaultSize(100)
{
    maxSize = (ms>defaultSize) ? ms : defaultSize;  
    heap=new Type[maxSize];
    currentSize=0;
}

template <class Type>
MinHeap<Type>::MinHeap(Type a[], int n):defaultSize(100)
{
    maxSize = (n>defaultSize) ? n : defaultSize;    
    heap=new Type[maxSize];
    currentSize=n;
    for (int i=0; i<n; i++) heap[i]=a[i];
    int curPos=(currentSize-2)/2;
    while (curPos>=0)
    {
        FilterDown(curPos, currentSize-1);  
        curPos--;
    }
}

template <class Type>
MinHeap<Type>::~MinHeap()
{
    delete []heap;  
}

template <class Type>
void MinHeap<Type>::FilterDown(const int start, const int endOfHeap)
{
    int i=start, j=i*2+1;   
    Type temp=heap[i];
    while (j<=endOfHeap)
    {
        if (j<endOfHeap&&heap[j]>heap[j+1]) j++;                                
        if (temp<heap[j]) break;                        
        else
        {
            heap[i]=heap[j];    
            i=j;
            j=2*i+1;
        }
    }
    heap[i]=temp;
}

template <class Type>
void MinHeap<Type>::FilterUp(const int start)
{
    int i=start, j=(i-1)/2; 
    Type temp=heap[i];
    while (i>0)
    {
        if (temp>=heap[j]) break;
        else
        {
            heap[i]=heap[j];    
            i=j;
            j=(i-1)/2;
        }
    }
    heap[i]=temp;
}

template <class Type>
bool MinHeap<Type>::RemoveMin(Type &x)
{
    if (IsEmpty())      
    {
        cerr<<"Heap empty!"<<endl;  
        return false;
    }
    x=heap[0];
    heap[0]=heap[currentSize-1];
    currentSize--;
    FilterDown(0, currentSize-1);
    return true;
}

template <class Type>
bool MinHeap<Type>::Insert(const Type &x)
{
    if (IsFull())       
    {
        cerr<<"Heap Full!"<<endl;   
        return false;
    }
    heap[currentSize]=x;
    FilterUp(currentSize);
    currentSize++;
    return true;
}

template <class Type>
bool MinHeap<Type>::IsEmpty()
{
    return currentSize==0;  
}

template <class Type>
bool MinHeap<Type>::IsFull()
{
    return currentSize==maxSize;    
}

template <class Type>
void MinHeap<Type>::MakeEmpty()
{
    currentSize=0;  
}

template <class Type>
int MinHeap<Type>::Size()
{
    return currentSize; 
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值