0037算法笔记——【分支限界法】最大团问题

        问题描述

     给定无向图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值,从而进一步搜索不可能得到更大的团,此时算法已找到一个最优解。

     算法具体实现如下:

     1、MaxHeap.h

  1. template<class T>  
  2. class MaxHeap  
  3. {  
  4.     public:  
  5.         MaxHeap(int MaxHeapSize = 10);  
  6.         ~MaxHeap() {delete [] heap;}  
  7.         int Size() const {return CurrentSize;}  
  8.   
  9.         T Max()   
  10.         {          //查   
  11.            if (CurrentSize == 0)  
  12.            {  
  13.                 throw OutOfBounds();  
  14.            }  
  15.            return heap[1];  
  16.         }  
  17.   
  18.         MaxHeap<T>& Insert(const T& x); //增   
  19.         MaxHeap<T>& DeleteMax(T& x);   //删   
  20.   
  21.         void Initialize(T a[], int size, int ArraySize);  
  22.   
  23.     private:  
  24.         int CurrentSize, MaxSize;  
  25.         T *heap;  // element array   
  26. };  
  27.   
  28. template<class T>  
  29. MaxHeap<T>::MaxHeap(int MaxHeapSize)  
  30. {// Max heap constructor.   
  31.     MaxSize = MaxHeapSize;  
  32.     heap = new T[MaxSize+1];  
  33.     CurrentSize = 0;  
  34. }  
  35.   
  36. template<class T>  
  37. MaxHeap<T>& MaxHeap<T>::Insert(const T& x)  
  38. {// Insert x into the max heap.   
  39.     if (CurrentSize == MaxSize)  
  40.     {  
  41.         cout<<"no space!"<<endl;   
  42.         return *this;   
  43.     }  
  44.   
  45.     // 寻找新元素x的位置   
  46.     // i——初始为新叶节点的位置,逐层向上,寻找最终位置   
  47.     int i = ++CurrentSize;  
  48.     while (i != 1 && x > heap[i/2])  
  49.     {  
  50.         // i不是根节点,且其值大于父节点的值,需要继续调整   
  51.         heap[i] = heap[i/2]; // 父节点下降   
  52.         i /= 2;              // 继续向上,搜寻正确位置   
  53.     }  
  54.   
  55.    heap[i] = x;  
  56.    return *this;  
  57. }  
  58.   
  59. template<class T>  
  60. MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)  
  61. {// Set x to max element and delete max element from heap.   
  62.     // check if heap is empty   
  63.     if (CurrentSize == 0)  
  64.     {  
  65.         cout<<"Empty heap!"<<endl;   
  66.         return *this;   
  67.     }  
  68.   
  69.     x = heap[1]; // 删除最大元素   
  70.     // 重整堆   
  71.     T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整   
  72.   
  73.     // find place for y starting at root   
  74.     int i = 1,  // current node of heap   
  75.        ci = 2; // child of i   
  76.   
  77.     while (ci <= CurrentSize)   
  78.     {  
  79.         // 使ci指向i的两个孩子中较大者   
  80.         if (ci < CurrentSize && heap[ci] < heap[ci+1])  
  81.         {  
  82.             ci++;  
  83.         }  
  84.         // y的值大于等于孩子节点吗?   
  85.         if (y >= heap[ci])  
  86.         {  
  87.             break;   // 是,i就是y的正确位置,退出   
  88.         }  
  89.   
  90.         // 否,需要继续向下,重整堆   
  91.         heap[i] = heap[ci]; // 大于父节点的孩子节点上升   
  92.         i = ci;             // 向下一层,继续搜索正确位置   
  93.         ci *= 2;  
  94.     }  
  95.   
  96.     heap[i] = y;  
  97.     return *this;  
  98. }  
  99.   
  100. template<class T>  
  101. void MaxHeap<T>::Initialize(T a[], int size,int ArraySize)  
  102. {// Initialize max heap to array a.   
  103.     delete [] heap;  
  104.     heap = a;  
  105.     CurrentSize = size;  
  106.     MaxSize = ArraySize;  
  107.   
  108.     // 从最后一个内部节点开始,一直到根,对每个子树进行堆重整   
  109.    for (int i = CurrentSize/2; i >= 1; i--)  
  110.    {  
  111.         T y = heap[i]; // 子树根节点元素   
  112.         // find place to put y   
  113.         int c = 2*i; // parent of c is target   
  114.                    // location for y   
  115.         while (c <= CurrentSize)   
  116.         {  
  117.             // heap[c] should be larger sibling   
  118.             if (c < CurrentSize && heap[c] < heap[c+1])  
  119.             {  
  120.                 c++;  
  121.             }  
  122.             // can we put y in heap[c/2]?   
  123.             if (y >= heap[c])  
  124.             {  
  125.                 break;  // yes   
  126.             }  
  127.   
  128.             // no   
  129.             heap[c/2] = heap[c]; // move child up   
  130.             c *= 2; // move down a level   
  131.         }  
  132.         heap[c/2] = y;  
  133.     }  
  134. }  
template<class T>
class MaxHeap
{
	public:
		MaxHeap(int MaxHeapSize = 10);
		~MaxHeap() {delete [] heap;}
        int Size() const {return CurrentSize;}

        T Max() 
		{          //查
           if (CurrentSize == 0)
		   {
                throw OutOfBounds();
		   }
           return heap[1];
        }

		MaxHeap<T>& Insert(const T& x); //增
		MaxHeap<T>& DeleteMax(T& x);   //删

		void Initialize(T a[], int size, int ArraySize);

	private:
		int CurrentSize, MaxSize;
		T *heap;  // element array
};

template<class T>
MaxHeap<T>::MaxHeap(int MaxHeapSize)
{// Max heap constructor.
	MaxSize = MaxHeapSize;
	heap = new T[MaxSize+1];
	CurrentSize = 0;
}

template<class T>
MaxHeap<T>& MaxHeap<T>::Insert(const T& x)
{// Insert x into the max heap.
	if (CurrentSize == MaxSize)
	{
		cout<<"no space!"<<endl; 
		return *this; 
	}

    // 寻找新元素x的位置
    // i——初始为新叶节点的位置,逐层向上,寻找最终位置
	int i = ++CurrentSize;
	while (i != 1 && x > heap[i/2])
	{
		// i不是根节点,且其值大于父节点的值,需要继续调整
		heap[i] = heap[i/2]; // 父节点下降
		i /= 2;              // 继续向上,搜寻正确位置
    }

   heap[i] = x;
   return *this;
}

template<class T>
MaxHeap<T>& MaxHeap<T>::DeleteMax(T& x)
{// Set x to max element and delete max element from heap.
	// check if heap is empty
	if (CurrentSize == 0)
	{
		cout<<"Empty heap!"<<endl; 
		return *this; 
	}

	x = heap[1]; // 删除最大元素
	// 重整堆
	T y = heap[CurrentSize--]; // 取最后一个节点,从根开始重整

	// find place for y starting at root
	int i = 1,  // current node of heap
	   ci = 2; // child of i

	while (ci <= CurrentSize) 
    {
		// 使ci指向i的两个孩子中较大者
		if (ci < CurrentSize && heap[ci] < heap[ci+1])
		{
			ci++;
		}
		// y的值大于等于孩子节点吗?
		if (y >= heap[ci])
		{
			break;   // 是,i就是y的正确位置,退出
		}

		// 否,需要继续向下,重整堆
		heap[i] = heap[ci]; // 大于父节点的孩子节点上升
		i = ci;             // 向下一层,继续搜索正确位置
		ci *= 2;
    }

	heap[i] = y;
	return *this;
}

template<class T>
void MaxHeap<T>::Initialize(T a[], int size,int ArraySize)
{// Initialize max heap to array a.
	delete [] heap;
	heap = a;
	CurrentSize = size;
	MaxSize = ArraySize;

	// 从最后一个内部节点开始,一直到根,对每个子树进行堆重整
   for (int i = CurrentSize/2; i >= 1; i--)
   {
		T y = heap[i]; // 子树根节点元素
		// find place to put y
		int c = 2*i; // parent of c is target
                   // location for y
		while (c <= CurrentSize) 
		{
			// heap[c] should be larger sibling
			if (c < CurrentSize && heap[c] < heap[c+1])
			{
				c++;
			}
			// can we put y in heap[c/2]?
			if (y >= heap[c])
			{
				break;  // yes
			}

			// no
			heap[c/2] = heap[c]; // move child up
			c *= 2; // move down a level
        }
		heap[c/2] = y;
	}
}
     2、6d6.cpp

  1. //最大团问题 优先队列分支限界法求解    
  2. #include "stdafx.h"   
  3. #include "MaxHeap.h"   
  4. #include <iostream>   
  5. #include <fstream>   
  6. using namespace std;  
  7.   
  8. const int N = 5;//图G的顶点数   
  9. ifstream fin("6d6.txt");     
  10.   
  11. class bbnode  
  12. {  
  13.     friend class Clique;  
  14.     private:  
  15.         bbnode *parent;     //指向父节点的指针   
  16.         bool LChild;        //左儿子节点标识   
  17. };  
  18.   
  19. class CliqueNode  
  20. {  
  21.     friend class Clique;  
  22.     public:  
  23.         operator int() const  
  24.         {     
  25.             return un;  
  26.         }  
  27.     private:  
  28.         int cn,         //当前团的顶点数   
  29.             un,         //当前团最大顶点数的上界   
  30.             level;      //节点在子集空间树中所处的层次   
  31.         bbnode *ptr;    //指向活节点在子集树中相应节点的指针   
  32. };  
  33.   
  34. class Clique  
  35. {  
  36.     friend int main(void);  
  37.     public:  
  38.         int BBMaxClique(int []);  
  39.     private:  
  40.         void AddLiveNode(MaxHeap<CliqueNode>&H,int cn,int un,int level,bbnode E[],bool ch);  
  41.         int **a,        //图G的邻接矩阵   
  42.             n;          //图G的顶点数   
  43. };  
  44.   
  45. int main()  
  46. {  
  47.     int bestx[N+1];  
  48.     int **a = new int *[N+1];    
  49.     for(int i=1;i<=N;i++)      
  50.     {      
  51.         a[i] = new int[N+1];      
  52.     }   
  53.       
  54.     cout<<"图G的邻接矩阵为:"<<endl;  
  55.     for(int i=1; i<=N; i++)    
  56.     {    
  57.         for(int j=1; j<=N; j++)    
  58.         {    
  59.             fin>>a[i][j];        
  60.             cout<<a[i][j]<<" ";      
  61.         }    
  62.         cout<<endl;    
  63.     }  
  64.   
  65.     Clique c;  
  66.     c.a = a;  
  67.     c.n = N;  
  68.   
  69.     cout<<"图G的最大团顶点个数为:"<<c.BBMaxClique(bestx)<<endl;  
  70.     cout<<"图G的最大团解向量为:"<<endl;  
  71.     for(int i=1;i<=N;i++)      
  72.     {     
  73.         cout<<bestx[i]<<" ";  
  74.     }   
  75.     cout<<endl;  
  76.   
  77.     for(int i=1;i<=N;i++)      
  78.     {     
  79.         delete[] a[i];     
  80.     }   
  81.     delete []a;  
  82.     return 0;  
  83. }  
  84.   
  85. //将活节点加入到子集空间树中并插入到最大堆中   
  86. void Clique::AddLiveNode(MaxHeap<CliqueNode> &H, int cn, int un, int level, bbnode E[], bool ch)  
  87. {  
  88.     bbnode *b = new bbnode;  
  89.     b->parent = E;  
  90.     b->LChild = ch;  
  91.   
  92.     CliqueNode N;  
  93.     N.cn = cn;  
  94.     N.ptr = b;  
  95.     N.un = un;  
  96.     N.level = level;  
  97.     H.Insert(N);  
  98. }  
  99.   
  100. //解最大团问题的优先队列式分支限界法   
  101. int Clique::BBMaxClique(int bestx[])  
  102. {  
  103.     MaxHeap<CliqueNode> H(1000);  
  104.   
  105.     //初始化   
  106.     bbnode *E = 0;  
  107.     int i = 1,  
  108.         cn = 0,  
  109.         bestn = 0;  
  110.   
  111.     //搜集子集空间树   
  112.     while(i!=n+1)//非叶节点   
  113.     {  
  114.         //检查顶点i与当前团中其他顶点之间是否有边相连   
  115.         bool OK = true;  
  116.         bbnode *B = E;  
  117.         for(int j=i-1; j>0; B=B->parent,j--)  
  118.         {  
  119.             if(B->LChild && a[i][j]==0)  
  120.             {  
  121.                 OK = false;  
  122.                 break;  
  123.             }  
  124.         }  
  125.   
  126.         if(OK)//左儿子节点为可行结点   
  127.         {  
  128.             if(cn+1>bestn)  
  129.             {  
  130.                 bestn = cn + 1;  
  131.             }  
  132.             AddLiveNode(H,cn+1,cn+n-i+1,i+1,E,true);  
  133.         }  
  134.   
  135.         if(cn+n-i>=bestn)//右子树可能含有最优解   
  136.         {  
  137.             AddLiveNode(H,cn,cn+n-i,i+1,E,false);  
  138.         }  
  139.   
  140.         //取下一扩展节点   
  141.         CliqueNode N;  
  142.         H.DeleteMax(N); //堆非空   
  143.         E = N.ptr;  
  144.         cn = N.cn;  
  145.         i = N.level;  
  146.     }  
  147.   
  148.     //构造当前最优解   
  149.     for(int j=n; j>0; j--)  
  150.     {  
  151.         bestx[j] = E->LChild;  
  152.         E = E->parent;  
  153.     }  
  154.   
  155.     return bestn;  
  156. }  
//最大团问题 优先队列分支限界法求解 
#include "stdafx.h"
#include "MaxHeap.h"
#include <iostream>
#include <fstream>
using namespace std;

const int N = 5;//图G的顶点数
ifstream fin("6d6.txt");   

class bbnode
{
	friend class Clique;
	private:
		bbnode *parent;		//指向父节点的指针
		bool LChild;		//左儿子节点标识
};

class CliqueNode
{
	friend class Clique;
	public:
		operator int() const
		{	
			return un;
		}
	private:
		int cn,			//当前团的顶点数
			un,			//当前团最大顶点数的上界
			level;		//节点在子集空间树中所处的层次
		bbnode *ptr;	//指向活节点在子集树中相应节点的指针
};

class Clique
{
	friend int main(void);
	public:
		int BBMaxClique(int []);
	private:
		void AddLiveNode(MaxHeap<CliqueNode>&H,int cn,int un,int level,bbnode E[],bool ch);
		int **a,		//图G的邻接矩阵
			n;			//图G的顶点数
};

int main()
{
	int bestx[N+1];
	int **a = new int *[N+1];  
    for(int i=1;i<=N;i++)    
    {    
        a[i] = new int[N+1];    
    } 
	
	cout<<"图G的邻接矩阵为:"<<endl;
	for(int i=1; i<=N; i++)  
    {  
        for(int j=1; j<=N; j++)  
        {  
            fin>>a[i][j];      
            cout<<a[i][j]<<" ";    
        }  
        cout<<endl;  
    }

	Clique c;
	c.a = a;
	c.n = N;

	cout<<"图G的最大团顶点个数为:"<<c.BBMaxClique(bestx)<<endl;
	cout<<"图G的最大团解向量为:"<<endl;
	for(int i=1;i<=N;i++)    
    {   
		cout<<bestx[i]<<" ";
    } 
	cout<<endl;

	for(int i=1;i<=N;i++)    
    {   
        delete[] a[i];   
    } 
	delete []a;
	return 0;
}

//将活节点加入到子集空间树中并插入到最大堆中
void Clique::AddLiveNode(MaxHeap<CliqueNode> &H, int cn, int un, int level, bbnode E[], bool ch)
{
	bbnode *b = new bbnode;
	b->parent = E;
	b->LChild = ch;

	CliqueNode N;
	N.cn = cn;
	N.ptr = b;
	N.un = un;
	N.level = level;
	H.Insert(N);
}

//解最大团问题的优先队列式分支限界法
int Clique::BBMaxClique(int bestx[])
{
	MaxHeap<CliqueNode> H(1000);

	//初始化
	bbnode *E = 0;
	int i = 1,
		cn = 0,
		bestn = 0;

	//搜集子集空间树
	while(i!=n+1)//非叶节点
	{
		//检查顶点i与当前团中其他顶点之间是否有边相连
		bool OK = true;
		bbnode *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(H,cn+1,cn+n-i+1,i+1,E,true);
		}

		if(cn+n-i>=bestn)//右子树可能含有最优解
		{
			AddLiveNode(H,cn,cn+n-i,i+1,E,false);
		}

		//取下一扩展节点
		CliqueNode N;
		H.DeleteMax(N); //堆非空
		E = N.ptr;
		cn = N.cn;
		i = N.level;
	}

	//构造当前最优解
	for(int j=n; j>0; j--)
	{
		bestx[j] = E->LChild;
		E = E->parent;
	}

	return bestn;
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你介绍一下分支限界法和装载问题的相关内容。 分支限界法是一种求解最优化问题的算法,通过不断扩展解空间,逐步缩小搜索范围,最终找到最优解。它的核心思想是将问题划分成许多子问题,并采用优先队列(或优先级队列)来维护待扩展的子问题集合,每次取出优先级最高的子问题进行扩展,直到找到最优解或者队列为空。 而装载问题是一种典型的分支限界法应用场景,它的主要思想是在给定的一些物品中选出尽可能多的物品放入容量为C的背包中,使得背包中物品的总重量不超过C,并且背包中物品的总价值最大。这个问题可以通过分支限界法来求解。 下面是一个简单的 Java 代码实现,用于解决装载问题: ```java import java.util.*; public class BranchAndBound { public static void main(String[] args) { int[] w = {5, 10, 20, 30}; // 物品的重量 int[] v = {50, 60, 140, 120}; // 物品的价值 int C = 50; // 背包的容量 int n = w.length; // 物品的数量 int[] x = new int[n]; // 记录每个物品是否被选中 PriorityQueue<Node> queue = new PriorityQueue<>(); queue.offer(new Node(-1, 0, 0)); // 将根节点加入队列中 while (!queue.isEmpty()) { Node node = queue.poll(); // 取出优先级最高的子问题 if (node.level == n - 1) { // 如果是叶子节点,更新最优解 for (int i = 0; i < n; i++) { x[i] = node.x[i]; } } else { int level = node.level + 1; int weight = node.weight; int value = node.value; if (weight + w[level] <= C) { // 左子节点表示选中当前物品 int[] left = Arrays.copyOf(node.x, n); left[level] = 1; queue.offer(new Node(level, weight + w[level], value + v[level], left)); } // 右子节点表示不选当前物品 queue.offer(new Node(level, weight, value, node.x)); } } int max = 0; for (int i = 0; i < n; i++) { if (x[i] == 1) { System.out.println("第" + (i + 1) + "个物品被选中"); max += v[i]; } } System.out.println("最大价值为:" + max); } // 子问题节点 static class Node implements Comparable<Node> { int level; // 当前节点所在的层级 int weight; // 当前节点的背包重量 int value; // 当前节点的背包价值 int[] x; // 记录每个物品是否被选中 public Node(int level, int weight, int value) { this.level = level; this.weight = weight; this.value = value; this.x = new int[0]; } public Node(int level, int weight, int value, int[] x) { this.level = level; this.weight = weight; this.value = value; this.x = x; } @Override public int compareTo(Node o) { return o.value - this.value; // 根据价值进行优先级比较 } } } ``` 希望这个简单的例子能帮助你更好地理解分支限界法和装载问题。如果你还有其他问题或者疑惑,欢迎随时向我提出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值