首先来看一下这三个知识点的概念
一、最小支配集
对于图G=(V,E)来说,最小支配集指的是从V中取尽量少的点组成一个集合,使得对于V中剩余的点都与取出来的点有边相连。也就是说,设V‘是图G的一个支配集,则对于图中的任意一个顶点u,要么属于集合V’,要么与V‘中的顶点相邻。在V’中出去任何元素后V‘不再是支配集,则支配集是极小支配集。称G的所有支配集中顶点个数最少的支配集为最小支配集,最小支配集中顶点的个数称为支配数。
二、最小点覆盖
对于图G=(V,E)来说,最小点覆盖指的是从V中取尽量少的点组成一个集合,使得E中所有的边都与取出来的点相连。也就是说,设V‘是图G的一个顶点覆盖,则对于图中的任意一条边(u,v),要么u属于集合V’,要么v属于集合V‘。在V‘中除去任何元素后V’不在是顶点覆盖,则V‘是极小顶点覆盖。称G的所有顶点覆盖中顶点个数最少的覆盖为最小点覆盖。
三、最大独立集
对于图G=(V,E)来说,最大独立集指的是从V中取尽量多的点组成一个集合,使得这些点之间没有边相连。也就是说,设V’是图G的一个独立集,则对于图中任意一条边(u,v),u和v不能同时属于集合V',甚至可以u和v都不属于集合V‘。在V’中添加任何不属于V‘元素后V’不再是独立集,则V‘是极大独立集。称G的所有顶点独立集中顶点个数最多的独立集为最大独立集。
自我感觉最小点覆盖和最小支配集容易弄混......第一次做题时就把最小支配集当成最小点覆盖做了...orz
最小支配集处理的是点,最小点覆盖处理的是边
要记住最小点覆盖在贪心的时候是不处理根节点的。
接下来是贪心处理
无论哪一个都是先深搜一遍得到遍历序列,然后倒着处理。
倒着处理使得每一个节点都是处理完了它的所有子节点后再来处理它,保证了贪心的正确性
最小支配集:
倒着遍历的时候,如果当前点既不属于支配集也没有被支配集里的点相连(即没有被覆盖),并且它的父节点也没有在支配集里,就将它的父节点加入到支配集里,ans++,将当前点,当前点的父节点,当前点的父节点的父节点标记(被覆盖了)
int p[N]; //父节点编号
int newpos[N]; //深度优先遍历中newpos[i]=x为编号为x的点是第i个被遍历的
bool vis[n]; //是否已经遍历过
int now; //现在已经有几个点被遍历
int n,m;
void dfs(int x) //搜索
{
newpos[++now]=x;
for(int i=head[x];i;i=e[i].ne){
int v=e[i].v;
if(!vis[v]){
vis[v]=1;
p[v]=x;
dfs(v);
}
}
}
bool se[N]; //se[i]=true表示第i个点在支配集集合中
bool s[N]; //s[i]=true表示I点已经被支配集覆盖
int gread()
{
int ans=0;
met(s,0);
met(se,0);
for(int i=n;i>=1;i--){ //贪心,倒着遍历
int v=newpos[i];
if(!s[v]){
if(!se[p[v]]){
se[p[v]]=1;
ans++;
}
s[v]=1;
s[p[v]]=1;
s[p[p[v]]]=1; //父节点的父节点显然也被覆盖
}
}
return ans;
}
附一个例题:POJ3659
#include
#include
#include
#include
#define exp 1e-8
#define mian main
#define pii pair
#define pll pair
#define ll long long
#define pb push_back
#define PI acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x) priority_queue
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=1e4+10;
int p[N],n,tot,now,newpos[N];
bool vis[N],s[N],se[N];
struct node
{
int v,ne;
}e[N*2];
int head[N];
void add(int u,int v)
{
e[++tot].v=v;
e[tot].ne=head[u];
head[u]=tot;
}
void dfs(int x)
{
newpos[++now]=x;
for(int i=head[x];i;i=e[i].ne){
int v=e[i].v;
if(!vis[v]){
vis[v]=1;
p[v]=x;
dfs(v);
}
}
}
int gread()
{
int ans=0;
memset(s,0,sizeof(s));
memset(se,0,sizeof(se));
for(int i=n;i>=1;i--){
int v=newpos[i];
if(!s[v]){
if(!se[p[v]]){
se[p[v]]=1;
ans++;
}
s[v]=1;
s[p[v]]=1;
s[p[p[v]]]=1;
}
}
return ans;
}
void init()
{
tot=0;
memset(head,0,sizeof(head));
memset(newpos,0,sizeof(newpos));
memset(vis,0,sizeof(vis));
memset(p,0,sizeof(p));
now=0;
}
int main()
{
while(~scanf("%d",&n)){
int x,y;
init();
for(int i=1;i
最小点覆盖:
这个不能处理根节点
如果当前点和当前点的父节点都没有被覆盖,就将当前点的父节点加入集合中,再标记当前点和当前点的父节点被覆盖
int gread()
{
memset(s,0,sizeof(s));
memset(Set,0,sizeof(Set));
int ans=0;
for(int i=n;i>1;i--){ //根不能处理
int v=newpos[i];
if(!s[v]&&!s[p[v]]){
Set[p[v]]=1;
ans++;
s[v]=1;
s[p[v]]=1;
}
}
return ans;
}
来一道例题:POJ1463
#include
#include
#include
#include
#define exp 1e-8
#define mian main
#define pii pair
#define pll pair
#define ll long long
#define pb push_back
#define PI acos(-1.0)
#define inf 0x3f3f3f3f
#define w(x) while(x--)
#define int_max 2147483647
#define lowbit(x) (x)&(-x)
#define gcd(a,b) __gcd(a,b)
#define pq(x) priority_queue
#define ull unsigned long long
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define pl(a,n) next_permutation(a,a+n)
#define ios ios::sync_with_stdio(false)
#define met(a,x) memset((a),(x),sizeof((a)))
using namespace std;
const int N=1e5+10;
int n,m,tot,now;
int newpos[N],head[N],vis[N],s[N],se[N],p[N];
struct node
{
int v,ne;
}e[N*2];
void add(int u,int v)
{
e[++tot].v=v;
e[tot].ne=head[u];
head[u]=tot;
}
void init()
{
now=0;
tot=0;
memset(newpos,0,sizeof(newpos));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
memset(p,0,sizeof(p));
}
void dfs(int x)
{
newpos[++now]=x;
for(int i=head[x];i;i=e[i].ne){
int v=e[i].v;
if(!vis[v]){
vis[v]=1;
p[v]=x;
dfs(v);
}
}
}
int gread()
{
memset(s,0,sizeof(s));
memset(se,0,sizeof(se));
int ans=0;
for(int i=n;i>=2;i--){
int v=newpos[i];
if(!s[v]&&!s[p[v]]){
se[p[v]]=1;
ans++;
s[p[v]]=1;
s[v]=1;
}
}
return ans;
}
int main()
{
while(~scanf("%d",&n)){
int x,y,z;
init();
for(int i=1;i<=n;i++){
scanf("%d:(%d)",&x,&y);
for(int j=1;j<=y;j++){
scanf("%d",&z);
add(x,z);
add(z,x);
}
}
vis[1]=1;
p[1]=1;
dfs(1);
printf("%d\n",gread());
}
return 0;
}
最大独立集:
如果当前节点没有被覆盖,将当前节点加入集合中,在标记当前点和当前点的父节点被覆盖
int gread()
{
memset(s,0,sizeof(s));
memset(Set,0,sizeof(Set));
int ans=0;
for(int i=n;i>=1;i--){
int v=newpos[i];
if(!s[v]){
Set[v]=1;
ans++;
s[v]=1;
s[p[v]]=1;
}
}
return ans;
}
最后要注意在深搜之前要处理一下根节点,标记数组vis先标记上根节点,父亲节点数组p[1]=1;
int main()
{
vis[1]=true;
p[1]=1;
dfs(1);
}