台州 OJ 1553 Rebuilding Roads 树状DP

题意:给一棵树,要求去掉最少的边得到一颗孤立的正好 P 个结点的树

第一次接触树状DP,瞎搞好久,看别人的代码看懂的,涨姿势了。

代码:

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

const int MAX = 155;
const int inf = 0x3fffffff; 

int n, p;
vector<int> G[MAX];        //
int tot[MAX];
int dp[MAX][MAX];        //子树 i 为 j 个结点时要去掉的最少的边数 
int ans;

int getTot(int root);    //求出每一棵子树一共有多少个结点
void dfs(int root);        //求解 dp 数组 

int main(){
    //freopen("input.txt", "r", stdin);
    ans = inf;
    memset(tot, 0, sizeof(tot));
    for(int i=0; i<MAX; i++)
        G[i].clear();
    
    cin >> n >> p; 
    for(int i=1; i<n; i++){
        int father, child;
        cin >> father >> child;
        G[father].push_back(child);
    }
    getTot(1);
    dfs(1);
    cout << ans;
}

int getTot(int root){
    int sum = 1;    //包括根 
    for(int i=0; i<G[root].size(); i++){
        sum += getTot(G[root][i]);
    }
    tot[root] = sum;
    return sum;
}

void dfs(int root){
    dp[root][1] = G[root].size();    //只有一个结点时,删掉所有孩子的边
    dp[root][tot[root]] = 0;
    for(int i=2; i<=p; i++){
        dp[root][i] = inf;
    }
    
    for(int i=0; i<G[root].size(); i++){
        int child = G[root][i];
        dfs(child);
        for(int j=tot[root]; j>=1; j--){        //取 j 个结点 (如果是从小到大,会对后面的有影响,dp[root][j-k] 可能包含在子树里面取了的情况,这时再在子树里取就出错了) 
            for(int k=1; k<=tot[child] && k<j; k++){    //子树取 k 个结点 
                dp[root][j] = min(dp[root][j], dp[root][j-k] + dp[child][k] - 1);
//                cout << root << " " << j << " " << k << " " << dp[root][j] << " " << child << endl;
            }
        }
    }
    
    ans = min(ans, dp[root][p] + (root!=1));
}
原文地址:https://www.cnblogs.com/lighter-blog/p/7229516.html