poj 1947

树形dp!!!关键是枚举s点的所有的不去掉k子树的情况!!!

 

因为是树形DP,而子问题往往涉及的是子树的信息,所以我们要对树先进行深搜,求出子问题的解,然后再返回根节点,求根节点的信息。。。

 

#include<iostream>
#include<stdio.h>
#include<string.h>
#define maxn 155
using namespace std;
int n,p;
int son[maxn],blo[maxn],hf[maxn];//son[i]为i的儿子blo[i]为i的兄弟,hf[i]表示i是否有父亲
int dp[maxn][maxn];//dp[i][j]表示以i为根得到j个点的子树(不包括根)所要删除的边的条数;
void dfs(int s){
 int i,j,k,temp;
 for(i=0;i<=p;i++) dp[s][i]=0xffff;
 dp[s][1]=0;
 k=son[s];
 while(k){//对于每一,如果去掉k子树,则dp[s][i]=dp[s][i]+1;
  //否者为dp[s][i]=min(min(dp[s][j]+dp[k][i-j]))这里就是双重dp!!!
  dfs(k);
  for(i=p;i>=0;i--){
   temp=dp[s][i]+1;
   for(j=0;j<=i;j++){
    if(dp[k][i-j]+dp[s][j]<temp)
       temp=dp[k][i-j]+dp[s][j];}
   dp[s][i]=temp;
  }
  k=blo[k];//根据他来确定其他点父亲
  }
 }
int solve(int root){
 dfs(root);
 int i,ans;
 ans=dp[root][p];
 for(i=1;i<=n;i++){
  if(dp[i][p]<ans)
  ans=dp[i][p]+1;//去掉子树与根i的边
  }
  return ans;
 }
int main(){
 while(scanf("%d%d",&n,&p)!=EOF){
  memset(hf,0,sizeof(hf));
  memset(blo,0,sizeof(blo));
  memset(son,0,sizeof(son));
  int a,b;
  for(int i=0;i<n-1;i++){
   cin>>a>>b;
   blo[b]=son[a];
   son[a]=b;
   hf[b]=1;
   
   }
   int root;
  for(int i=1;i<=n;i++) if(!hf[i]) root=i;//没有父节点的是根;
  
  cout<<solve(root)<<endl;
  
 }
 return 0;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值