题目链接
题意:给你一棵树,问你如果最后只剩下一个p个点的子树,那么至少要切多少刀。
思路:树上dp的经典题,设dp[i][j]为以i为根节点,剩下j个节点的子树时至少要切的刀数,那么转移为dp[i][j]=min(dp[i][j],dp[i][j-k]+dp[v][k]-1)(其中v为i的儿子,k从1取到j)(这就相当于,将i为根的子树与v为根的子树拼接起来,-1剪掉的是他们iv连接的那一刀,这刀不需要砍)。枚举根节点比较答案时,记得非原根节点的要+1,把这个根节点从原来树上砍下来的这一刀。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include<stdio.h> #include<algorithm> #include<string.h> #include<math.h> #include<vector> using namespace std; int INF=0x3f3f3f3f; vector<int > g[300]; int dp[300][300]; int num[300]; int sum[300]; void dfs(int u) { int l=g[u].size(); dp[u][1]=l; sum[u]=1; for(int i=0;i<l;i++) { dfs(g[u][i]); sum[u]+=sum[g[u][i]]; for(int j=sum[u];j>=1;j--) for(int k=1;k<j;k++) dp[u][j]=min(dp[u][j],dp[g[u][i]][k]+dp[u][j-k]-1); } } int main() { int n,p; while(~scanf("%d%d",&n,&p)) { memset(sum,0,sizeof(sum)); memset(dp,INF,sizeof(dp)); for(int i=1;i<=n;i++)g[i].clear(); for(int i=1;i<=n-1;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); num[v]++; } int root; for(int i=1;i<=n;i++)if(num[i]==0)root=i; dfs(root); int ans=dp[root][p]; for(int i=1;i<=n;i++) { if(i!=root)ans=min(ans,dp[i][p]+1); } printf("%d\n",ans); } return 0; }
|