树形dp专题

推荐一个博主写的——关于树形DP中求最小支配集,覆盖集,独立集的博客

他写的关于树形DP代码的解释很好懂

虽然还有求重心,树形背包什么的没讲,不过对于我这样刚入门的人来说应该是够了

 

最小支配集——POJ3659

题目含义

有1到N个点,给出点两两的相邻关系,这个图的最小支配集

题目分析

简单易懂的最小支配集问题,看着链接的博客很快就能写出来

但博客没讲一些细节问题

比如说遇到一个无向图可以用vis数组使无向变有向,这样dfs也只用一个参数就行了

还有根节点没有父节点,不能在求最小时加入dp[root][2]

题目代码

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
struct node{
    int to;
    int next;
}edge[maxn<<1];
int head[maxn],dp[maxn][3];
int n,a,b,tot;
bool vis[maxn];
int min3(int a,int b,int c){
    return min(a,min(b,c));
}
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u){
    vis[u]=true;
    dp[u][0]=1;
    dp[u][1]=dp[u][2]=0;
    int inc=INF;
    bool exist=false;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(!vis[v]){
            dfs(v);
            dp[u][0]+=min3(dp[v][0],dp[v][1],dp[v][2]);
            dp[u][2]+=min(dp[v][0],dp[v][1]);
            if(dp[v][0]<=dp[v][1]){
                exist=true;
                dp[u][1]+=dp[v][0];
            }
            else{
                inc=min(inc,dp[v][0]-dp[v][1]);
                dp[u][1]+=dp[v][1];
            }
        }
    }
    if(!exist){
        dp[u][1]+=inc;
    }
}
int main(){
    scanf("%d",&n);
    memset(dp,0,sizeof(dp));
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    tot=0;
    for(int i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    dfs(a);
    printf("%d\n",min(dp[a][0],dp[a][1]));
    return 0;
}
最小支配集

 

 

最小覆盖集——POJ1463

题目含义

给出N个点,以及每个点所相邻的点,求最小覆盖集

题目分析

与支配集相比只有两个状态,还不用讨论,简单多了

题目代码

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=1e4+7;
const int INF=0x3f3f3f3f;
struct node{
    int to;
    int next;
}edge[maxn<<1];
int head[maxn],dp[maxn][2];
int tot,t,n,m,a,root;
char ch;
void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u){
    dp[u][0]=0;
    dp[u][1]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        dfs(v);
        dp[u][0]+=dp[v][1];
        dp[u][1]+=min(dp[v][0],dp[v][1]);
    }
}
int main(){
    while(~scanf("%d",&t)){
        memset(dp,0,sizeof(dp));
        memset(head,-1,sizeof(head));
        tot=0;
        for(int i=0;i<t;i++){
            scanf("%d%c%c%d%c",&n,&ch,&ch,&m,&ch);
            for(int j=0;j<m;j++){
                scanf("%d",&a);
                add(n,a);
            }
            if(!i)root=n;
        }
        dfs(root);
        printf("%d\n",min(dp[root][0],dp[root][1]));
    }
    return 0;
}
最小覆盖集

一遍过真是爽啊

转载于:https://www.cnblogs.com/helman/p/11265911.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值