题意:给你一棵n个节点的树,问你取出一棵有p个节点的子树最少要去掉几条边。
思路:简单的树形背包
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct{//链式前向星 int v,next; }edge[320]; int head[160]; int dp[160][160];//dp[i][j]表示在i节点的子树里(不是以i组成的子树)去掉j个节点需要减少的最少的边数 int sum[160];//sum[i]表示在i节点的子树里一个有多少个节点(不包括i) int cnt; void add(int u,int v){ edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++; } /* i节点的子树(不包括i) 以i节点组成子树(树)(包括i) */ int n,m; void dfs(int k){ sum[k]=0;//初始化 for(int i=1;i<=n;i++)//初始化 dp[k][i]=1e9; dp[k][0]=0;//无论是哪个点,去掉0个节点的花费一定是0 for(int i=head[k];i!=-1;i=edge[i].next){ dfs(edge[i].v);//先向子节点搜索 sum[k]+=sum[edge[i].v]+1;//计算k节点的子树里有多少个节点 dp[edge[i].v][sum[edge[i].v]+1]=1;//去除当前子节点组成树的所有的节点的花费一定是1(只需断开k与当前子节点的连接) //printf("%d %d %d ",edge[i].v,sum[edge[i].v]+1,dp[edge[i].v][sum[edge[i].v]+1]) ; for(int j=n;j>0;j--){ //为什么要j>0,因为j=0的花费一定是0,没必要更新 for(int l=1;l<=j;l++) dp[k][j]=min(dp[k][j],dp[k][j-l]+dp[edge[i].v][l]); /*为什么l从1开始,因为从0开始没必要(min(dp[k][j],dp[k][j]+dp[edge[i].v][0]);)它的 结果一定是原来的dp[k][j],因为 dp[edge[i].v][0]=0,为什么l可以等于j,因为我可以当前的j个全部 从当前子节点组成的子树去除*/ } } } bool vt[160];//标记数组 int main(){ while(scanf("%d%d",&n,&m)!=EOF){ int u,v; cnt=0; fill(head,head+152,false);//初始化 fill(head,head+152,-1); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v); vt[v]=true; } int root=0; for(int i=1;i<=n&&!root;i++){//找根节点 if(!vt[i]) root=i; } dfs(root); int ans=dp[root][n-m];//我要得到节点数为m的子树,需要去掉n-m个点 for(int i=1;i<=n;i++) if(sum[i]+1>=m) ans=min(ans,dp[i][sum[i]+1-m]+1);//取以第i个节点为根节点组成的子树(包括i)时,先要减去他与父节点的连接, //然后再到子树里减去sum[i]+1-m个节点(因为以i为根组成的子树(树)只有sum[i]+1个节点) printf("%d ",ans); } return 0; }
上面是我看别人的思路写的,下面是我过一个礼拜自己再写的
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int maxn=160; const int INF=1e9; struct{ int v,next; }edge[maxn*2]; int head[maxn]; int n,m,cnt; int dp[maxn][maxn]; int sum[maxn]; int ans; void add(int u,int v){ edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++; } void dfs(int k,int fz){ dp[k][0]=0; sum[k]=1; for(int i=1;i<=n-m;i++) dp[k][i]=INF; for(int i=head[k];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==fz) continue; dfs(v,k); sum[k]+=sum[v]; for(int j=min(sum[k],n-m);j>=1;j--){ for(int j1=1;j1<=j;j1++){ dp[k][j]=min(dp[k][j],dp[v][j1]+dp[k][j-j1]); } } } if(sum[k]>=m){ ans=min(ans,dp[k][sum[k]-m]+(fz!=0)); } dp[k][sum[k]]=1; } int main(){ int u,v; scanf("%d%d",&n,&m); fill(head,head+2+n,-1); ans=INF; for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1,0); printf("%d ",ans); return 0; }