最小支配集 最小点覆盖 最大独立集
图G=(V,E)
定义:
最小支配集
:从V中取尽量少的点组成一个集合使得V中剩下的点都与取出来的点有边相连
最小点覆盖
:从V中取尽量少的点组成一个集合使得E中所有的边都与取出来的点相连
最大独立集
:从V中取尽量少的点组成一个集合使得这些点之间没有边相连
对于图中的最下支配集、最小点覆最大独立集问题是一个NP的问题,但在树中可以通过贪心或动态规划的方法很快求得。
贪心
这三个问题得贪心策略都是先求出树得dfs序然后逆序贪心,逆序是为了保证在处理某个节点得时候它的儿子都已经处理
最小支配集贪心策略
:按照反向dfs序进行贪心得过程中,对于一个既不属于支配集也不与支配集中顶点相连的点我们将它的父亲加入支配集。
最小点覆盖贪心策略:
如果当前点和和当前点的父节点都不属于覆盖集合,则将父节点加入到顶点覆盖集合
最大独立集合贪心策略:
如果当前节点没有被覆盖,则将当前节点加入独立集,并标记当前节点和其父节点都被覆盖。
【例题一:树的最小支配集】
-
POJ 3659 Cell Phone Network
求一颗树的最小支配集的点的数目
-
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
#define LL long long int
const int MAXN=20005;
int n;
int a[MAXN];
int fa[MAXN];//fa[i]表示节点i的父亲
int index;
int tid[MAXN];//tid保存dfs序
bool select[MAXN];//表示是否选了
bool cover[MAXN];//表示是否被覆盖
struct Edge
{
int v;
int next;
}edge[MAXN*2];
int edgecount;
int head[MAXN];
void Init()
{
memset(head,-1,sizeof(head));
edgecount=0;
index=0;
memset(select,0,sizeof(select));
memset(cover,0,sizeof(cover));
}
void Add_edge(int u,int v)
{
edge[++edgecount].v=v;
edge[edgecount].next=head[u];
head[u]=edgecount;
}
void Dfs(int u)
{
tid[++index]=u;
for(int k=head[u];k!=-1;k=edge[k].next)
{
int v=edge[k].v;
if(v==fa[u])continue;
fa[v]=u;
Dfs(v);
}
}
int Greedy()//贪心算法求解树的最小支配集
{
int ans=0;
for(int i=n;i>=1;i--)
{
int u=tid[i];
int f=fa[u];
if(cover[u]==0)//如果当前点未被它的子节点覆盖
{
if(select[f]==0)//父亲节点未被选,结合前一个条件也就是既不属于支配集也不与支配集中顶点相连
{
select[f]=1;
ans++;
cover[u]=1;
cover[f]=1;
cover[fa[f]]=1;
}
}
}
return ans;
}
int main()
{
int u,v;
while(scanf("%d",&n)!=EOF)
{
Init();
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
Add_edge(u,v);
Add_edge(v,u);
}
Dfs(1);
printf("%d\n",Greedy());
}
return 0;
}
【例题二:树的最小点覆盖】
-
POJ 1463 Strategic game
求一颗树的最小点覆盖的点的数目
-
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
#define LL long long int
const int MAXN=20005;
int n;
int a[MAXN];
int fa[MAXN];//fa[i]表示节点i的父亲
int index;
int tid[MAXN];//tid保存dfs序
bool select[MAXN];//表示是否选了
struct Edge
{
int v;
int next;
}edge[MAXN*2];
int edgecount;
int head[MAXN];
void Init()
{
memset(head,-1,sizeof(head));
edgecount=0;
index=0;
memset(select,0,sizeof(select));
}
void Add_edge(int u,int v)
{
edge[++edgecount].v=v;
edge[edgecount].next=head[u];
head[u]=edgecount;
}
void In()
{
int x,y,m;
for(int i=1;i<=n;i++)
{
scanf("%d:(%d)",&x,&m);
x++;
for(int j=1;j<=m;j++)
{
scanf("%d",&y);
y++;
Add_edge(x,y);
Add_edge(y,x);
}
}
}
void Dfs(int u)//dfs初始化dfs序和父亲数组
{
tid[++index]=u;
for(int k=head[u];k!=-1;k=edge[k].next)
{
int v=edge[k].v;
if(v==fa[u])continue;
fa[v]=u;
Dfs(v);
}
}
int Greedy()//贪心算法求解树的最小点覆盖
/*
处理到某个点的时候保证这个点下面的边都已经处理完了
*/
{
int ans=0;
for(int i=n;i>1;i--)//根节点不用访问
{
int u=tid[i];
int f=fa[u];
if(select[u]==0 && select[f]==0)
{
select[f]=1;
ans++;
}
}
return ans;
}
int main()
{
int u,v;
while(scanf("%d",&n)!=EOF)
{
Init();
In();
Dfs(1);
printf("%d\n",Greedy());
}
return 0;
}
【例题三:数的最大独立集】