问题描述:
什么是无向图的最大割?
有无向图G=(V,E).设U是V的一个子集。对任意的顶点u,v如果(u,v)是E中的一条边,如果u在U中,则v一定不
在U中,这样的边称为顶点集合U的一个割边。那个这个顶点集合U所有的割边就构成图G的一个割。最大割
的意思,就是说含割边最多的割。
编程任务:
给定一个无向图G,计算G的最大割。
数据输入:
第一行2个整数 n,e表示顶点个数和边个数。下面e行是边,每行2个整数对应顶点编号。顶点编号从1-n;
数据输出:
第一行是最大割的边数。
第二行是顶点集的向量对应1-n,0表示不在集合,1表示在集合。
解题思想:
这道题也是一个子集选取问题,所以它的解空间树是一个子集树。这个集合n个元素,每个元素可以有
选取或不选取2种选择,所以共用2^n个子集。
这里用分支界限法。
分支界限法:类似宽度优先搜索,它和回溯的区别在与,它每次都取一个节点,然后把它的所有的儿子节点
都扩展,然后加到队列中,所以说,分支界限法,这种扩展方式导致没个节点最多一次扩展机会,它在扩展
它的分支的时候,根据一定的界限条件,或淘汰不可能产生最优解的分支。
这里的队列有2种,一种是普通队列就是先进先出。
另一种就是优先级队列,它会根据优先级进行出队,这和Windows中的消息队列类似。
在C++中,STL中priority_queue就是优先级队列。queue是普通队列。优先级队列可以用最大堆或最小堆
实现。
#include <iostream>
#include <queue>
using namespace std;
int n,e;//顶点数和边数
int Graph[200][200];//存储图的邻接矩阵
int bestcut = 0;//存储最优解
int bestx[200];//存储最优解
struct HeapNode//队列节点
{
HeapNode()
{
memset(x,0,sizeof(x));
}
int i;
int cut;
int edges;
int x[200];
booloperator < (const HeapNode&a)const
{
returncut<a.cut;
}
};
void maxcut()
{
HeapNodeE;
priority_queue<HeapNode> Q;
E.cut = 0;
E.edges = e;
E.i = 0;
while (1)
{
if (E.i==n)
{
if (E.cut>bestcut)
{
bestcut = E.cut;
memcpy(bestx,E.x,sizeof(int)*n);
}
}
else
{
HeapNodetemp = E;
int i =E.i;
for (intj=0;j<n;j++)
{
if (Graph[i][j])
{
if (temp.x[j])
{
temp.cut–;
}
else
{
temp.cut++;
temp.edges–;
}
}
}
temp.x[i] =1;
temp.i++;
Q.push(temp);
if(E.cut+E.edges>bestcut)//界限条件
{
E.i++;
Q.push(E);
}
}
if(Q.size()==0)//队列为空跳出循环
{
break;
}
E = Q.top();
Q.pop();
}
}
int main()
{
intu,v;
cin>>n>>e;
for (int i=0;i<e;i++)
{
cin>>u>>v;
Graph[u-1][v-1] = 1;
Graph[v-1][u-1] = 1;
}
maxcut();
cout<<bestcut<<endl;
for (int k=0;k<n;k++)
{
cout<<bestx[k]<<”;
}
cout<<endl;
return 0;
}
输入实例
7 18
1 4
1 5
1 6
1 7
2 3
2 4
2 5
2 6
2 7
3 4
3 5
3 6
3 7
4 5
4 6
5 6
5 7
6 7
输出实例:
12
1 1 1 0 1 0 0