Gomory-Hu tree是一颗代表了所有源目节点对间的最小割的树。求解出Gomory-Hu tree就可以了解两两节点对之间的最大流(最大流最小割定理)。举例:
下图左侧为一无向图,右侧为初始Gomory-Hutree(所有点在统一集合中),下面进行Gomory-Hu tree的求解。
步骤一:任意选定一个源节点和一个目的节点。在本例中不失一般性选择节点1为源节点(s),5为目的节点(t)。则可得最大流为6,且最小割相应的将点分为如下图右侧所示的两个集合。
步骤二:任意选定与之前步骤不同的一个源节点和一个目的节点。在本例中不失一般性选择节点3为源节点(s),5为目的节点(t)。由于0124四个节点已经被视作一个集合,则可得最大流为8,且最小割相应的将点分为如下图右侧所示的三个集合。
步骤三:任意选定与之前步骤不同的一个源节点和一个目的节点。在本例中不失一般性选择节点1为源节点(s),2为目的节点(t)。同上可得最大流为6,且最小割相应的将点分为如下图右侧所示的四个集合。
重复以上步骤可以将原无向图划分为一棵Gomory-Hutree,如下图所示。
通过此求解过程可知,代码实现整个步骤是十分复杂的。1990年Dan Gusfield通过"Very Simple Methods for All Pairs Network Flow Analysis"一文提出了一种容易实现的Gomory-Hutree的求解方法,也是本文采用的实现方法。下面通过例子来介绍这种实现方法:
不失一般性,举例原图是拥有6个节点的无向图,节点间的权重皆为1,节点间的最小割如下图所示:
步骤一:创建一棵星型树,节点1为中心节点,其他节点为叶子节点,如下图左侧所示。
步骤二:分别选编号为2至6的节点为源节点(s),重复做步骤三和步骤四。
步骤三:在星型树中令与s节点相邻的节点为目的节点(t),计算s与t之间的最大流,并由此得到最小割。将最大流标注在星型树中s节点与t节点间的链路上。
步骤四:对于每一个编号大于s的节点i,如果在原图中s与i是邻居,且i与s在同一割集中,则去除星型图中i与t的连接,增加i与s的连接,如下图中间所示。
最后可得到如上图右侧所示的Gomory-Hutree。
下面附上实现代码以供参考:
#include "CLVMMinKCut.h"
using namespace std;
CLVMMinKCut::CLVMMinKCut()
{
}
CLVMMinKCut::~CLVMMinKCut()
{
}
int CLVMMinKCut::MinKCut(int D[][MAXNUMBER],int n)
{
m_n=n;
m_Flag=0;
for (int i = 0; i < m_n; i++)
for (int j = 0; j < m_n; j++)
m_Tree[i][j]=0;
for (int i = 0; i < m_n; i++) //初始化得到星形树,得到树根点
{
for (int k = i+1; k < m_n; k++)
if (D[i][k]!=0)
{
m_Flag=1;
break;
}
if (m_Flag==1)
{
for (int j = i+1; j < m_n; j++)
{
m_Tree[i][j]=m_Tree[j][i]=1;
}
m_Flag=i;
break;
}
}
for (int i = m_Flag; i < m_n; i++) //得到树叶子节点
{
m_Flag1=0;
for (int j = m_Flag; j < m_n; j++)
if (D[i][j]!=0)
{
m_Flag1=1;
break;
}
if (m_Flag1==0)
m_Tree[i][m_Flag]=m_Tree[m_Flag][i]=0;
}
//ShowInfo(); //显示初始化星型树
for (int i = 0; i < n; i++) //读入业务量矩阵
for (int j = 0; j < n; j++)
m_D[i][j]=D[i][j];
GetGomoryHuTree();
return 0;
}
int CLVMMinKCut::GetGomoryHuTree()
{
CLMaxFlow *MaxFlow=new CLMaxFlow;
for (int i = 0; i < m_n; i++) //do 论文中3,4步
for (int j = i+1; j < m_n; j++)
{
cout<<i<<"~"<<j<<"\n";
if (m_Tree[i][j]!=0)
{
MaxFlow->SetCLMaxFlow(m_D,m_n,j,i); //最大流,得到最小割,并将
m_Tree[i][j]=m_Tree[j][i]=MaxFlow->m_MaximumFlow;
for (int k = j+1; k < m_n; k++) //every node larger than s
if (m_Tree[k][i]!=0&&MaxFlow->FindSourceNumber(k)) //if is a neighbor of t(m_Tree中), and is on the s side(原图中)
{
m_Tree[k][i]=m_Tree[i][k]=0; //disconnecting i from t
m_Tree[k][j]=m_Tree[j][k]=1; //connecting i to s
}
}
}
//ShowInfo();
return 0;
}
int CLVMMinKCut::ShowInfo()
{
cout<<"m_Tree:"<<"\n";
for (int i = 0; i < m_n; i++)
{
for (int j = 0; j < m_n; j++)
cout<<m_Tree[i][j]<<"\t";
cout<<"\n";
}
return 0;
}