题意:
给定n个节点的无向树,删边得到一个K节点的子树。
问:最少需要删几条边。
第一行给定n,K(<=150)
第二行给出 u v 表示 u是v的父节点
思路:
树形背包
dp[i][j] 表示以i为根的子树中 删边得到j个节点的子树需要删边数量。
对于一个节点i,先计算出i所有子节点的答案。
初始化dp[i][1]为子节点个数,意思是删掉所有子节点。
则加入子树v后会少删一条边。
加入子树v中j个节点,和原先树中的k个节点合并 共得到 j+k个节点,花费为 dp[ now ][ k+j ] = min(dp[ v ][ j ]+ dp[ now ][ k ] -1);
#include<stdio.h> #include<string.h> #include<iostream> #include<vector> using namespace std; #define N 155 #define ll int #define inf 100000000 inline int min(int a,int b){return a>b?b:a;} ll dp[N][N];//dp[i][j]表示以i点为根,生成一个j个点的树需要切割的最小边数 ll n, K, root; bool father[N]; vector<int>G[N]; void init(){ for(int i = 1; i <= n; i++) for(int j = 0; j <= n; j++)dp[i][j] = inf; for(int i = 0; i <= n; i++)G[i].clear(); memset(father, 0, sizeof(father)); } void dfs(int x,int fa){ if(x==fa)dp[x][1] = G[x].size(); //注意这个状态。 else dp[x][1] = G[x].size()-1; for(int i = 0; i < G[x].size();i++) { int v = G[x][i]; if(v == fa)continue; dfs(v,x); } for(int i = 0; i < G[x].size(); i++) for(int k = K-1; k >= 1; k--)if(dp[x][k] < inf) { int v = G[x][i]; if(v==fa)continue; for(int j = 1; j <= K-1; j++) if(dp[v][j]<inf) dp[x][k+j] = min(dp[x][k]+dp[v][j]-1, dp[x][k+j]); } } int main(){ ll i, j, u, v; while(~scanf("%d%d",&n,&K)){ init(); for(i=1;i<n;i++){ scanf("%d %d",&u,&v); G[u].push_back(v); G[v].push_back(u); father[v] = true; } for(i = 1; i <= n; i++)if(!father[i])root = i; dfs(root,root); int ans = dp[root][K]; for(i = 1; i <= n; i++)ans = min(ans, dp[i][K]+1); printf("%d\n", ans); } return 0; }
原文:http://blog.csdn.net/acmmmm/article/details/19752893