题目网址:http://poj.org/problem?id=1947;
大意:给定n个点、n-1条边的一棵树,求最少删除多少边使得此时这棵树恰剩有p个结点。
打算今天刚开始做会树形DP,于是就搜到了这道题。也算一道入门题。既然是到DP,显然就可以设状态F[R][i]表示使以R为根节点的子树剩有i个子节点的最小代价。然后考虑它的所有儿子节点s:只有选或者不选;
选的时候:F[R][i]=min(F[s][i-j]+F[R][j])(1<=j<i);
不选的时候:F[R][i]=F[R][i]+1;
整理一下就变成了:F[R][i]=min(F[s][i-j]+F[R][j],F[R][i]+1).最后扫一遍F[1..n][p]的最小值即可。注:打擂台时,除了根节点,其余子节点的F[i][p]需要+1,因为它要先与根节点断开联系,要先删一条边。(本来我也没想到,经大神提醒)。
代码:
/* Author: ALXPCUN PROG: poj 1947 DATE: 2015.3.12 */ #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <algorithm> #define REP(i,s,n) for(int i=s;i<=n;i++) #define REP_(i,s,n) for(int i=n;i>=s;i--) #define CLR(a,b) memset(a,b,sizeof(a)) using namespace std; const int MAX_N=150+10; int n,p,F[MAX_N][MAX_N],P[MAX_N],R; bool Used[MAX_N]; void DP(int R); int Solve(); int main(){ scanf("%d%d",&n,&p); REP(i,1,n-1){ int s,t; scanf("%d%d",&s,&t); P[t]=s; Used[t]=1; } REP(i,1,n) if(!Used[i]) R=i; CLR(Used,0); printf("%d\n",Solve()); //system("pause"); return 0; } void DP(int R){ REP(i,1,n) F[R][i]=10000; F[R][1]=0; REP(k,1,n){ if(!Used[k]&&P[k]==R){ DP(k); REP_(i,1,p){ int tmp=F[R][i]+1; REP(j,1,i-1) tmp=min(tmp,F[k][i-j]+F[R][j]); F[R][i]=tmp; } } } } int Solve(){ DP(R); int ans=F[R][p]; REP(i,1,n) ans=min(ans,F[i][p]+1); return ans; }
时间: 2024-12-21 20:11:11