题意:给一棵树,要求去掉最少的边得到一颗孤立的正好 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)); }