最小权顶点覆盖问题
问题描述:
给定一个赋权无向图G=(V,E),每个顶点v∈V都有一个权值w(v)。如果U包含于V,且对于(u,v)∈E 有u∈U 且v∈V-U,则有v∈K.如:U = {1}, 若有边(1,2), 则有2属于K. 若有集合U包含于V使得U + K = V,就称U 为图G 的一个顶点覆盖。G 的最小权顶点覆盖是指G 中所含顶点权之和最小的顶点覆盖。
算法设计:
a. 给定的图G 有n 个顶点和m条边,用w[i]存储顶点i的权值,用e[i][j]标记两顶
点为i和j的边是否存在,用c[i]标记顶点i是否在顶点覆盖集中;
b. 用函数cover()判断图G 是否被顶点覆盖(用t标记):
① 初始t=0;
② 采用while循环对每个顶点i(1≤i≤n)进行讨论:
1> 若顶点i不在顶点覆盖集中(即c[i]==0),则查找与之有边连接的顶点j(即e[i][j]==1),判断所有顶点j:
若存在顶点j在顶点覆盖集中(即c[j]==0),则t=1;
若所有顶点j都不在顶点覆盖集中(即t==0),则图G 未被顶点
覆盖(return 0);
2> 当i>n时循环结束;
③ return 1;
c. 用递归函数cut(i, s) 来实现回溯法搜索子集树(形式参数i表示递归深度,n用
来控制递归深度,形式参数s表示当前顶点权之和):
① 若s>=bestw,则不是最优解,剪去相应子树,返回到i-1层继续执行;
② 若i >n,则算法搜索到一个叶结点,调用函数cover()对图G进行判断:
若cover()为真,则用bestw对最优解进行记录,返回到i-1层继续执行;
③ 对顶点i分在与不在顶点覆盖集中两种情况进行讨论:
1> 若顶点i不在顶点覆盖集中(即c[i]==0),则调用函数cut(i+1,s);
2> 若顶点i在顶点覆盖集中(即c[i]==1),则调用函数cut(i+1,s+w[i]);
④ 当i=1时,若已测试完所有顶点覆盖方案,外层调用就全部结束;
d. 主函数调用一次cut(1,0)即可完成整个回溯搜索过程,最终得到的bestw即为所
求最小顶点权之和。
源程序:
#include<stdio.h>
#define MIN 100000
int m,n,u,v,bestw;
int e[100][100],w[100],c[100];
int cover()
{
int i,j,t;
i=1;
while (i<=n)
{
t=0;
if(c[i]==0)
{
j=1;
while(j<i)
{
if(e[j][i]==1&&c[j]==1)
t=1;
j++;
}
j++;
while(j<=n)
{
if(e[i][j]==1&&c[j]==1)
t=1;
j++;
}
if(t==0)
return 0;
}
i++;
}
return 1;
}
void cut(int i,int s)
{
if(s>=bestw)
return;
if(i>n)
{
if(cover())
bestw=s;
return;
}
c[i]=0;
cut(i+1,s);
c[i]=1;
cut(i+1,s+w[i]);
}
main()
{
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&w[i]);
c[i]=0;
}
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
e[i][j]=0;
for(k=1;k<=m;k++)
{
scanf("%d%d",&u,&v);
e[u][v]=1;
}
bestw=MIN;
cut(1,0);
printf("%d",bestw);
return 0;
}