poj 3659 Cell Phone Network (树最小支配集)

本文介绍如何使用树型动态规划(DP)方法解决求解树上最小支配集的问题,通过定义三种状态并实现相应的状态转移方程,最终得出最小支配数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目求一棵树的最小支配数。

支配集,即把图的点分成两个集合,所有非支配集内的点都和支配集内的某一点相邻。

听说即使是二分图,最小支配集的求解也是还没多项式算法的。而树上求最小支配集树型DP就OK了。

树上的每个结点作为其子树的根可以有三个状态:

  1. 不属于支配集且还没被支配
  2. 不属于支配集但被其孩子支配
  3. 属于支配集

那么就是用dp[u][1\2\3]来作为动归的状态,表示结点u为根子树的且u状态为1、2、3的最小支配数。

123转移该怎么转移就怎么转移。。最后的结果就是min(dp[root][2],dp[root][3])。

要注意的是对于有些结点前2个状态可能是不存在的,比如叶子结点不存在第2个状态、存在孩子是叶子结点的结点不存在第1个状态,这些不存在的状态要在转移的时候处理。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 123456
#define MAXN 111111
struct Edge{
    int u,v,next;
}edge[MAXN<<1];
int NE,head[MAXN];
void addEdge(int u,int v){
    edge[NE].u=u; edge[NE].v=v; edge[NE].next=head[u];
    head[u]=NE++;
}
int d[MAXN][3];
int dp(int u,int k,int fa){
    if(d[u][k]!=-1) return d[u][k];
    int res=0,diff=INF; bool flag=0,isLeaf=1;
    for(int i=head[u]; i!=-1; i=edge[i].next){
        int v=edge[i].v;
        if(v==fa) continue;
        isLeaf=0;
        if(k==0){
            if(dp(v,1,u)==INF) return d[u][k]=INF;
            res+=dp(v,1,u);
        }else if(k==1){
            if(dp(v,2,u)<=dp(v,1,u)){
                res+=dp(v,2,u);
                flag=1;
            }else{
                if(dp(v,1,u)==INF) return d[u][k]=INF;
                res+=dp(v,1,u);
                diff=min(diff,dp(v,2,u)-dp(v,1,u));
            }
        }else{
            res+=min(min(dp(v,0,u),dp(v,1,u)),dp(v,2,u));
        }
    }
    if(k==1 && isLeaf) return d[u][k]=INF;
    if(k==1 && !flag) res+=diff;
    return d[u][k]=res+(k==2);
}
int main(){
    int n,a,b;
    scanf("%d",&n);
    NE=0;
    memset(head,-1,sizeof(head));
    for(int i=1; i<n; ++i){
        scanf("%d%d",&a,&b);
        addEdge(a,b); addEdge(b,a);
    }
    memset(d,-1,sizeof(head));
    printf("%d",min(dp(1,1,0),dp(1,2,0)));
    return 0;
}

/**
dp[i][0] = sum(min(dp[s][2],dp[s][1]))自己没被选,父节点被选中
dp[i][1] = sum(min(dp[s][1],dp[s][2]))自己没被选,某儿子结点被选中
dp[i][2] = sum(min(dp[s][0],dp[s][1],dp[s][2]))自己被选中
求dp[i][1] 时需注意当所有的dp[s][1] < dp[s][2]时是不满足条件的。
*/

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define L(i) i<<1
#define R(i) i<<1|1
#define INF  0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-9
#define maxn 100010
#define MOD 1000000007
struct Edge
{
    int to,next;
} edge[maxn<<1];
int n,dp[maxn][3];
int tot,head[maxn];
void add(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void dfs(int u,int pre)
{
    dp[u][0] = 0;
    dp[u][1] = 0;
    dp[u][2] = 1;
    int flag = 0,Min = INF,cnt = 0,k = 0;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == pre)
            continue;
        k = 1;
        dfs(v,u);
        dp[u][0] += min(dp[v][2],dp[v][1]);
        dp[u][2] += min(dp[v][0],min(dp[v][1],dp[v][2]));
        if(dp[v][1] < dp[v][2])
        {
            dp[u][1] += dp[v][1];
            if(Min > dp[v][2])
            {
                Min = dp[v][2];
                cnt = dp[v][1];
            }
        }
        else
        {
            flag = 1;
            dp[u][1] += dp[v][2];
        }
    }
    if(!k)
    {
        dp[u][1] = INF;
        return;
    }
    if(!flag)
        dp[u][1] += Min - cnt;
}
int main()
{
    int t,C = 1;
    //scanf("%d",&t);
    while(scanf("%d",&n) != EOF)
    {
        int a,b;
        tot = 0;
        memset(head,-1,sizeof(head));
        for(int i = 1; i < n; i++)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
        }
        memset(dp,-1,sizeof(dp));
        dfs(1,-1);
        printf("%d\n",min(dp[1][1],dp[1][2]));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值