隔离村庄(树形dp[01背包])

隔离村庄

题目描述

有 n 个村庄,通过 n-1 条双向路连通。Jyb 同学财大气粗,想要买其中的恰好 k 个村庄。为了做一些秘密的事情,他想要通过破坏原来的路,在保证自己的 k 个村庄相互连通的情况下,把这 k 个村庄与其他村庄隔离开。请问他应该买哪 k 个村庄,最少破坏多少条路


Solution

dp[i][j]dp[i][j]表示以ii结点为子树, 制造大小为jj联通块所需要断的最少数目边数
dp[i][j]=min{dp[i][pj]+dp[to][p]}dp[i][j] = min{dp[i][p-j] + dp[to][p]} 为转移方程

初值 dp[i][1]=0dp[i][1] = 0 表示该节点为光棍时的状态,随着子树的遍历, 该值会随着改变,即转移时注意新的子树对先前dpdp值的影响, 体现在代码注释处


Code

#include<bits/stdc++.h>
#define reg register

const int maxn = 255;

int N, K, num;
int head[maxn], size[maxn], num_1[maxn], Tmp[maxn], dp[maxn][maxn];

struct Edge{ int nxt, to; } edge[maxn << 1];

void Add(int from, int to){
        edge[++ num] = (Edge){ head[from], to };
        head[from] = num;
}

void DFS(int k, int fa){
        size[k] = 1, num_1[k] = 0;
        dp[k][1] = 0;
        for(reg int i = head[k]; i; i = edge[i].nxt){
                int to = edge[i].to;
                if(to == fa) continue ;
                DFS(to, k);
                size[k] += size[to];
                /*
                for(reg int j = size[k]; j >= 1; j --)
                        for(reg int p = 1; p <= size[to]; p ++)
                                dp[k][j] = std::min(dp[k][j], dp[k][j-p] + dp[to][p]);
                */
                for(reg int j = size[k]; j >= 1; j --){
                        dp[k][j] ++;                    //新的儿子对过去状态的影响
                        for(reg int p = 1; p < j; p ++)
                                dp[k][j] = std::min(dp[k][j], dp[to][j - p] + dp[k][p]);
                }
        }
}

int main(){
        freopen("isolate.in", "r", stdin);
        freopen("isolate.out", "w", stdout);
        scanf("%d%d", &N, &K);
        for(reg int i = 1; i < N; i ++){
                int u, v;
                scanf("%d%d", &u, &v);
                Add(u, v), Add(v, u);
        }
        memset(dp, 0x3f, sizeof dp);
        DFS(1, 0);
        /*
        int Ans = 0x3f3f3f3f;
        for(reg int i = 1; i <= N; i ++) Ans = std::min(Ans, std::min(dp[i][N-K], dp[i][K]));
        */
        int Ans = dp[1][K];
        for(reg int i = 2; i <= N; i ++) Ans = std::min(Ans, dp[i][K] + 1);
    //    printf("%d
", dp[2][5]);
        printf("%d
", Ans);
        return 0;
}
原文地址:https://www.cnblogs.com/zbr162/p/11822680.html