树形dp基础题

树形dp理解

该dp的实质就是利用树的性质,儿子节点返回的值可以被父亲节点加以利用以及处理,这样就构成了一种在树结构上的动态规划,简称树形dp。从昨天开始决定刷树形dp专题,刷了4道水题,因此想写一个博客总结一下。

POJ 1463 Strategic game

这道题就是在树上的节点添加守卫,很明显我们可以得出以下结论,如果父亲节点不添加守卫,那么儿子节点一定要添加守卫,如果父亲节点添加了守卫,那么儿子节点可以选择添加或者不添加守卫。即:

dp[fa][0]+=dp[son][1];dp[fa][1]+=min(dp[son][1],dp[son][0]);

这里我用数组的第二维表示添加守卫还是没有添加守卫,0表示没有添加,1表示添加了守卫。给出AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1505;
int tot;

int head[maxn],dp[maxn][3],vis[maxn];

struct Edge{
    int to,next;
}e[maxn<<1];

void addedge(int from, int to){
    e[tot].to = to;
    e[tot].next = head[from];
    head[from] = tot++;
}

void dfs(int u){
    vis[u] = 1;
    dp[u][0] = 0;
    dp[u][1] = 1;
    for(int i = head[u]; i != -1; i = e[i].next){
        int v = e[i].to;
        if(!vis[v]){
            dfs(v);
            dp[u][0] += dp[v][1];
            dp[u][1] += min(dp[v][1], dp[v][0]);
        }
    }
}

int main(){
    int n,m;
    while(scanf("%d", &m) != EOF){
        int from, to;
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        while(m--){
            scanf("%d:(%d)", &from, &n);
            while(n--){
                scanf("%d", &to);
                addedge(from, to);
                addedge(to, from);
            }
        }
        dfs(0);
        printf("%d\n", min(dp[0][0], dp[0][1]));
    }
    return 0;
}

POJ 2378 Tree Cutting

这道题就是简单的dfs,感觉没有用到什么dp的思想,判断儿子节点数有没有超过总结点数的一半,判断除去以该点为根的子树节点数剩下的节点有没有超过总结点数的一半,这样很容易得出结论。

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10005;

struct Edge{
    int to,next;
}e[maxn<<1];

int N,siz[maxn],head[maxn],vis[maxn],half,ok[maxn],tot;
vector <int> vec;

void addedge(int from,int to){
    e[tot].to = to;
    e[tot].next = head[from];
    head[from] = tot++;
}

void dfs(int now){
    siz[now] = 1;
    vis[now] = 1;
    for(int u = head[now]; u != -1; u = e[u].next){
        int v = e[u].to;
        if(!vis[v]){
            dfs(v);
            if(siz[v] > half) ok[now] = 1;
            siz[now] += siz[v];
        }
    }
    if(N - siz[now] > half) ok[now] = 1;
}

int main(){
    while(scanf("%d", &N) != EOF){
        int from,to;
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        memset(ok, 0, sizeof(ok));
        memset(siz, 0, sizeof(siz));
        for(int i = 1; i < N; i++){
            scanf("%d%d", &from, &to);
            addedge(from, to);
            addedge(to, from);
        }
        half = N/2;
        dfs(1);
        for(int i = 1; i <= N; i++){
            if(!ok[i]) printf("%d\n", i);
        }
    }
    return 0;
}

POJ 3140 Contestants Division

与上题基本无差别。但是注意要用long long,并且abs函数要重写,因为long long类型不支持math.h中的abs

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 100005;
const int maxm = 1000005;
const LL INF = 0x3f3f3f3f3f3f3f3f;
struct Edge{
    int to,next;
}e[maxm<<1];

int head[maxn],vis[maxn],tot;
LL MIN,total,val[maxn];

LL Abs(LL a,LL b){
    if(a > b) return a-b;
    else return b-a;
}

void addedge(int from, int to){
    e[tot].to = to;
    e[tot].next = head[from];
    head[from] = tot++;
}

void dfs(int u){
    vis[u] = 1;
    for(int i = head[u]; i != -1; i = e[i].next){
        int v = e[i].to;
        if(!vis[v]){
            dfs(v);
            val[u] += val[v];
        }
    }
    LL temp = Abs(val[u], total-val[u]);
    MIN = min(MIN, temp);
}

int main(){
    int N,M,kase = 1;
    while(scanf("%d%d", &N, &M) != EOF){
        if(!M && !N) break;
        total = 0;
        for(int i = 1; i <= N; i++){
            scanf("%I64d", &val[i]);
            total += val[i];
        }
        int from,to;
        tot = 0;
        memset(head, -1, sizeof(head));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= M; i++){
            scanf("%d%d", &from, &to);
            addedge(from, to);
            addedge(to, from);
        }
        MIN = INF;
        dfs(1);
        printf("Case %d: %I64d\n", kase++, MIN);
    }
    return 0;
}

SGU 134 Centroid

这道题不想提了,sb题,找了半天bug,多组数据还不给过,写的一头包。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 16005;
const int INF = 0x3f3f3f3f;
int head[maxn],vis[maxn],son[maxn],siz[maxn],vec[maxn],tot,N;

struct Edge{
    int to,next;
}e[maxn<<1];

void addedge(int from,int to){
    e[tot].to = to;
    e[tot].next = head[from];
    head[from] = tot++;
}

void dfs(int u){
    vis[u] = 1;
    siz[u] = 1;
    for(int i = head[u]; i != -1; i = e[i].next){
        int v = e[i].to;
        if(!vis[v]){
            dfs(v);
            siz[u] += siz[v];
            if(son[u] < siz[v]) son[u] = siz[v];
        }
    }
    son[u] = max(son[u], N-siz[u]);
}


int main(){
    scanf("%d", &N);
    int from,to;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
    memset(siz, 0, sizeof(siz));
    memset(son, 0, sizeof(son));
    memset(vec, 0, sizeof(vec));
    tot = 0;
    for(int i = 1; i < N; i++){
        scanf("%d%d", &from, &to);
        addedge(from, to);
        addedge(to, from);
    }
    dfs(1);
    int ans = INF,len = 0;
    for(int i = 1; i <= N; i++){
        if(son[i] < ans){
            len = 0;
            vec[len++] = i;
            ans = son[i];
        }
        else if(son[i] == ans) vec[len++] = i;
    }
    printf("%d %d\n",ans,len);
    for(int i = 0; i < len; i++)
        if(i != len-1) printf("%d ", vec[i]);
        else printf("%d\n", vec[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值