樹上的最小支配集、最小點覆蓋、最大獨立集(貪心解決)

首先來看一下這三個知識點的概念

一、最小支配集

對於圖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<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#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<x>
#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<n;i++){
            scanf("%d%d",&x,&y);
          add(x,y);
          add(y,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]&&!s[p[v]]){
            Set[p[v]]=1;
            ans++;
            s[v]=1;
            s[p[v]]=1;
        }
    }
    return ans;
}

來一道例題:POJ1463 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define exp 1e-8
#define mian main
#define pii pair<int,int>
#define pll pair<ll,ll>
#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<x>
#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);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章