题意:给出一颗n个节点的树,这道题数据都是以1为根;
给出一个p,表示留下p个节点,要我们求出留下p个节点删除边的数量最少是多少
思路:树形dp dp【i】【j】
表示i这颗子树保留j个节点所需要删除边的数量最少的权值
那么dp【i】【1】就是把他所有儿子全部去掉,那么就是他son【】的个数
求出来后,会发现,这个i节点可能还有父亲,所以,在计算最后权值的时候,需要再+1;
那么,如何算呢?
我们采用dfs的方式,遍历到节点i的时候,假如计算出他的儿子节点k
那么就将儿子的节点数加到此节点上,然后开始枚举最优值
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e3+10; 4 const int inf=0x3f3f3f3f; 5 struct node 6 { 7 int v,nxt; 8 }G[maxn]; 9 int dp[maxn][maxn]; //表示在以i为根的情况下,保留j个节点需要的最少操作 10 //不过这里没有考虑其父亲,所以最后需要ans+1; 11 int son[maxn]; 12 int head[maxn];int num; 13 int sum[maxn]; 14 void add(int u,int v) 15 { 16 G[++num].v=v;G[num].nxt=head[u];head[u]=num; 17 } 18 void dfs(int u) 19 { 20 sum[u]=1; 21 for(int i=head[u];i;i=G[i].nxt){ 22 int v=G[i].v; 23 dfs(v); 24 sum[u]+=sum[v]; //记录个数 25 for(int i=sum[u];i>=0;i--){ 26 for(int j=1;j<i;j++){ //要在这颗子树上枚举,自然从1开始 27 //为何要小于i? 28 //因为这两棵树要连在一起,肯定要有父亲的边 29 //所以不能所有的边都从这颗子树上拿 30 //但是假如枚举的边的数量超过子节点的数量怎么办呢? 31 //没事,我们赋值为无穷大了,倘若枚举到这样一个数,也无法转移状态 32 dp[u][i]=min(dp[u][i],dp[v][j]+dp[u][i-j]-1); 33 } 34 } 35 } 36 } 37 int main() 38 { 39 int n,p; 40 scanf("%d%d",&n,&p); 41 for(int i=1;i<n;i++){ 42 int u,v; 43 scanf("%d%d",&u,&v); 44 add(u,v); 45 son[u]++; //记录儿子数量 46 } 47 memset(dp,inf,sizeof(dp)); 48 for(int i=1;i<=n;i++) dp[i][1]=son[i]; //此子树只保留根节点时需要删除的边数 49 dfs(1); 50 int ans=dp[1][p]; //1为根节点,没父亲 51 for(int i=2;i<=n;i++) 52 ans=min(ans,dp[i][p]+1); //其他节点有父亲,假如以他为答案,就需要减去其父亲 53 printf("%d\n",ans); 54 return 0; 55 }