树的最小支配集,最小点覆盖与最大独立集

最小支配集

定义1:对于图G=(V,E)来说,最小支配集指的是从V中取尽量少的点组成一个集合,使得对于V中剩余的点都与取出来的点有边相连。也就是说,设V‘是图G的一个支配集,则对于图中的任意一个顶点u,要么属于集合V’,要么与V‘中的顶点相邻。在V’中出去任何元素后V‘不再是支配集,则支配集是极小支配集。称G的所有支配集中顶点个数最少的支配集为最小支配集,最小支配集中顶点的个数称为支配数。

最小点覆盖

定义2:对于图G=(V,E)来说,最小点覆盖指的是从V中取尽量少的点组成一个集合,使得E中所有的边都与取出来的点相连。也就是说,设V‘是图G的一个顶点覆盖,则对于图中的任意一条边(u,v),要么u属于集合V’,要么v属于集合V‘。在V‘中除去任何元素后V’不在是顶点覆盖,则V‘是极小顶点覆盖。称G的所有顶点覆盖中顶点个数最少的覆盖为最小点覆盖。

最大独立集

定义3对于图G=(V,E)来说,最大独立集指的是从V中取尽量多的点组成一个集合,使得这些点之间没有边相连。也就是说,设V’是图G的一个独立集,则对于图中任意一条边(u,v),u和v不能同时属于集合V',甚至可以u和v都不属于集合V‘。在V’中添加任何不属于V‘元素后V’不再是独立集,则V‘是极大独立集。称G的所有顶点独立集中顶点个数最多的独立集为最大独立集。 

对于任意图G来说,最小支配集,最小点覆盖和最大独立集问题不存在多项式时间的解法。不过如果图G是一棵树,求上述三种特殊的集合还是很容易的。两种主要解法,贪心和树形动态规划。

首先先介绍贪心法,(以最小支配集为例)。

以最小支配集为例,对于树上的最小支配集问题,贪心策略是首先选择一点为根,按照深度优先遍历得到遍历序列,按照所得序列的反向序列的顺序进行贪心,对于一个既不属于支配集也不与支配集中的点相连的点来说,如果他的父节点不属于支配集,将其父节点加入支配集。

这里注意到贪心的策略中贪心的顺序非常重要,按照深度优先遍历得到遍历序列的反向进行贪心,可以保证对于每个点来说,当期子树都被处理过后才轮到该节点的处理,保证了贪心的正确性。

1.以1号点深度优先搜索整棵树,求出每个点在DFS中的编号和每个点的父亲节点编号。 
2.按DFS的反向序列检查,如果当前点既不属于支配集也不与支配集中的点相连,且它的父亲也不属于支配集,将其父亲点加入支配集,支配集个数加1。 
3.标记当前结点、当前结点的父节点(属于支配集)、当前结点的父节点的父节点(与支配集中的点相连)
上述就是贪心法的步骤。

int s[MAX],nodeset[MAX];    //s[MAX]数组是判断点是否被支配集内的点所覆盖,nodeset[MAX]是支配集集合,一般这两个数组设置成bool型,我这里是int型
int newpos[MAX];            //DFS时用到存储DFS序的数组
int now;int fa[MAX];        //fa[MAX]是用来存放各店父节点是谁

void DFS(int start) {
	newpos[now++] = start;
	for (int i = head[start]; i != -1; i = edge[i].next) {
		int v = edge[i].to;
		if (!vis[v]) {
			vis[v] = 1;
			fa[v] = start;
			DFS(v);
		}
	}
}
int greedySet(int n) {        //形参是树上点的个数
	/*int s[MAX] = { 0 };
	int nodeset[MAX] = { 0 };*/
	memset(s, 0, sizeof(s));
	memset(nodeset, 0, sizeof(nodeset));
	int ans = 0;
	for (int i = n - 1; i >= 0; i--) {        //逆序DFS序
		int t = newpos[i];
		if (!s[t]) {                        //如果当前点t未在支配集覆盖,即既不在支配集也不与支配集中的点相连
			if (!nodeset[fa[t]]) {    //其t结点的父亲也不在支配集中
				nodeset[fa[t]] = 1;    //将父节点加入支配集,支配集容量加一
				ans++;
			}
			s[t] = 1;
			s[fa[t]] = 1;
			s[fa[fa[t]]] = 1;    //标记当前点、当前结点的父节点、当前结点的父节点的父节点
		}
	}
	return ans;
}

最小点覆盖也可使用贪心算法

int greedy(int n)
{
    bool s[maxn]={0};
    bool set[maxn]={0};
    int ans=0;
    int i;
    for(i=n-1;i>=1;i--)
    {
        int t=newpos[i];
        if(!s[t]&&s[p[t]])
        {
            set[p[t]]=true;
            ans++;
            s[t]=true;
            s[p[t]]=true;
        }
    }
    return ans;
}

最大独立集

int greedy(int n)
{
    bool s[maxn]={0};
    bool set[maxn]={0};
    int ans=0;
    int i;
    for(i=n-1;i>=0;i--)
    {
        int t=newpos[i];
        if(!s[t])
        {
            set[t]=true;
            ans++;
            s[t]=true;
            s[p[t]]=true;
        }
    }
    return ans;
}

树形动态规划的做法,日后补充。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值