1. 程式人生 > >樹的最小支配集、最小點覆蓋、最大獨立集 (貪心orDP)

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

樹的最小支配集:點集中取出儘量少的點,使剩下的點與取出來的點都有邊相連。 樹的最小點覆蓋:點集中取出儘量少的點,使得所有邊都與選出的點相連。 樹的最大獨立集:點集中取出儘量多的點,使得這些點兩兩之間沒有邊相連。

貪心模板:

#include<bits/stdc++.h>
#define ll long long
#define sl(x) scanf("%lld",&x)
using namespace std;
const int N = 1e6+5;
struct node{
	int next,v;
}p[N];
int head[N],cnt,vis[N],now = 0;
ll fa[N],deep[N],n;
void init()
{
	for(int i = 0;i < n+1;i++) head[i] = -1;
	cnt = 0;
}
void dfs(int u,int f)
{
	fa[u] = f;
	deep[now++] = u;
	for(int i = head[u];~i;i = p[i].next)
	{
		if(p[i].v == f) continue;
		dfs(p[i].v,u);
	}
}

ll s[N];
void add(int u,int v)
{
	p[cnt].v = v;
	p[cnt].next = head[u];
	head[u] = cnt++;
}
int main()
{
	ll i,j,k,x,y;
	sl(n);
	init();
	for(i = 0;i < n-1;i++)
	{
		sl(x);sl(y);
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	//求解
}

最小支配集:

ll ans = 0;
for(i = now-1;i >= 0;i--)
{
	ll u = deep[i];
	if(!vis[u])
	{
		if(!s[fa[u]])
		{
			s[fa[u]] = 1;
			ans++;
		}
		vis[u] = 1;
		vis[fa[u]] = 1;
		vis[fa[fa[u]]] = 1;
	}
}

最小點覆蓋:

ll ans = 0;
for(i = now-1;i >= 0;i--)
{
	ll u = deep[i];
	if(!vis[u] && ! vis[fa[u]])
	{
		s[fa[u]] = 1;
		ans++;
		vis[u] = 1;
		vis[fa[u]] = 1;
	}
}
printf("%lld\n",ans);

最大獨立集:

ll ans = 0;
for(i = now-1;i >= 0;i--)
{
	ll u = deep[i];
	if(!vis[u])
	{
		s[u] = 1;
		ans++;
		vis[u] = 1;
		vis[fa[u]] = 1;
	}
}
printf("%lld\n",ans);

DP:

最小支配集: dp[i][0]: i屬於支配集,並且以點i為根的子樹都被覆蓋了的情況下的最少點數 dp[i][1]: i不屬於支配集,並且以i為根的子樹都被覆蓋,並且i被其中不少於1個子節點覆蓋的情況下的最少點數 dp[i][2]: i不屬於支配集,並且以i為根的子樹都被覆蓋,並且i沒被子節點覆蓋的情況下的最少點數

void solve(int u,int fa)
{
    dp[u][2] = 0;
    dp[u][0] = 1;
    int s = 0,sum = 0,inc = mod;
    for(int i = head[u];~i;i = p[i].next)
    {
        int v = p[i].v;
        if(v == fa) continue;
        solve(v,u);
        dp[u][0] += min(dp[v][0],min(dp[v][1],dp[v][2]));
        if(dp[v][0] <= dp[v][1])
        {
            sum += dp[v][0];
            s = 1;
        }
        else
        {
            sum += dp[v][1];
            inc = min(inc,dp[v][0]-dp[v][1]);
        }
        if(dp[v][1] != Mod && dp[u][2] != mod) dp[u][2] += dp[v][1];
        else dp[u][2] = Mod;
        if(inc == mod && !s) dp[u][1] = Mod;
        else
        {
            dp[u][1] = sum;
            if(!s) dp[u][1] += inc;
        }
    }
}

最小點覆蓋: dp[i][0]: i屬於點集合,並且以點i為根的自述中所連線的邊都被覆蓋的情況下點覆蓋集所包含的最少點數 dp[i][1]: i不屬於點集合,並且以點i為根的自述中所連線的邊都被覆蓋的情況下點覆蓋集所包含的最少點數

void solve(int u,int fa)
{
    dp[u][0] = 1;
    dp[u][1] = 0;
    for(int i = head[u];~i;i = p[i].next)
    {
        int v = p[i].next;
        if(v = fa) continue;
        solve(v,u);
        dp[u][0] += min(dp[v][0],dp[v][1]);
        dp[u][1] += dp[v][0];
    }
}

最大獨立集: dp[i][0]: 點i屬於獨立集的情況下,最大獨立集中點的個數 dp[i][1]: 點i屬於獨立集的情況下,最大獨立集中點的個數

void solve(int u,int fa)
{
    dp[u][0] = 1;
    dp[u][1] = 0;
    for(int i = head[u];~i;i = p[i].next)
    {
        int v = p[i].v;
        if(v == fa) continue;
        solve(v,u);
        dp[u][0] += dp[v][1];
        dp[u][1] += max(dp[v][0],dp[v][1]);
    }
}