简单的树形dp
题意: 给出一个树, 现在想得到一个有p个节点的子树,至少需要删除几条边。
思路: 给出一个树并且还要求一个最小值很容易想到要用树形dp。
我开的dp方程 dp[i][j]表示以i为父节点有j个节点的子树需要删掉几条边。
这个方程我是这样推的。 当面对一个子树的时候, 如果不要这个子树的东西,那么需要消耗1, 如果要了的话那么就是一个背包dp。 这样的话就在中间加一个中间变量数组。详见代码;
View Code
#include <iostream> #include <cstdio> #include <cstring> #include <string> using namespace std; const int N = 155, INF = 1<<29; struct EDGE { int u, v, next; }edge[N*2]; int num, head[N], dp[N][N], n, p; void add(int u, int v) { edge[num].u = u; edge[num].v = v; edge[num].next = head[u]; head[u] = num++; } void init() { num = 0; memset(head, -1, sizeof(head)); int u, v; for(int i=1; i<n; i++) { scanf("%d%d", &u, &v); add(u, v); add(v, u); } } void dfs(int u, int pre) { dp[u][1] = 0; int v; int g[N]; for(int i=head[u]; i!=-1; i=edge[i].next) { v = edge[i].v; if(v == pre) continue; for(int j=1; j<=p; j++) g[j] = n; dfs(v, u); for(int j=p; j>=1; j--) { for(int k=1; k<=p; k++) { if(j+k > p) break; g[j+k] = min(g[j+k], dp[u][j]+dp[v][k]); } } for(int j=1; j<=p; j++) dp[u][j] = min(dp[u][j]+1,g[j]); } } void solve() { for(int i=1; i<=n; i++) { for(int j=0; j<=p; j++) dp[i][j] = n; } dfs(1,-1); int ans = dp[1][p]; for(int i=2; i<=n; i++) ans = min(ans, dp[i][p]+1); printf("%d\n",ans); } int main() { while(scanf("%d%d", &n, &p) != EOF) { init(); solve(); } return 0; }


浙公网安备 33010602011771号