这一道题是树形dp,前段时间学习dp时,就想把树形dp给学了,当时不知从何下手,简单学了下依赖背包,就撤了,没怎么学树形dp。直到前几天我们做比赛,当我们遇到一道树形dp时,三个人没一个会的,三人对树形dp顶多是只有个概念吧。为了以后不那么悲剧,我决定还是学点树形dp吧。可是两三天只有一点点进步。废话不说了,说这道题。
这道题是给你一个有n个节点树,要你保留p个节点,求删除的最少边数。
dp[x][k]表示以x为保留k个节点最少要删除的边数
状态转移方程dp[x][j]=min(dp[x][j],dp[y][k]+dp[x][j-k]);
树形dp也许就是建立在搜索的dp
最后的结果是dp[root][p]和dp[i][p]+1的最小值,因为i不是根节点的话,还需要删掉一条边把它分离出来
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 200
const int oo = 1<<29 ;
bool in[N];
int dp[N][N],h[N];
typedef struct{
int to,next;
}E;
E edge[N];
int n,p;
int tol;
void Add_edge(int u,int v){
edge[tol].to=v;
edge[tol].next=h[u];
h[u]=tol++;
}
void dfs(int x){
int i,j;
for(i=1;i<=p;i++)
dp[x][i]=oo;
dp[x][1]=0;
for(i=h[x];i!=-1;i=edge[i].next){
int y=edge[i].to;
dfs(y);
for(j=p;j>=1;j--){//
dp[x][j]++;//要删除y节点,边数加1
for(int k=1;k<j;k++)
dp[x][j]=min(dp[x][j],dp[y][k]+dp[x][j-k]);
//表示以x为根,选j个节点要删去的最少边数
}
}
}
int main(){
while(cin>>n>>p){
tol=0;
int u,v;
memset(h,-1,sizeof(h));
memset(in,false,sizeof(in));
for(int i=1;i<n;i++){
cin>>u>>v;
in[v]=true;
Add_edge(u,v);//加边
}
int root;
for(int i=1;i<=n;i++){
if(!in[i]){
root=i;
break;
}
}
dfs(root);
int ans=dp[1][p];
for(int i=1;i<=n;i++)
ans=min(ans,dp[i][p]+1);
cout<<ans<<endl;
}
}
希望自己通过这道题我有些解决树形dp的概念