POJ 1947樹形DP
阿新 • • 發佈:2019-01-04
題目:
給你一棵樹,求最少剪掉幾條邊使能夠得到一個p個節點的樹。
解題思路:
dp[i][j]代表以i為根的子樹要變成j個節點需要剪掉的邊數
d[i][j]
= max(dp[i][j], dp[i][k]+dp[son][j-k]);
注意最後的選擇中,非根節點的要加上減去連線父節點的那條邊。
// Poj 1947 Rebuilding Roads // // //題目大意:給定一棵節點數為n的樹,問從這棵樹最少刪除幾條邊使得某棵子樹的節點個數為p,1<=n<=150,1<=p<=n。 // // //是二維。。。 //所謂分組揹包的部分在哪裡。。?!!!! //過鳥。。。 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string.h> #include<algorithm> #define inf 0x3fffffff #define maxn 300 #define maxm 300 using namespace std; int n; int em; int head[maxn]; int dp[maxn][maxn],du[maxn]; int sum[maxn],chu[maxn]; void init() { em=0; memset(head,-1,sizeof(head)); for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) dp[i][j]=inf; memset(du,0,sizeof(du)); memset(chu,0,sizeof(chu)); } struct node { int u,v,next; }edge[maxm]; void addedge(int u,int v) { edge[em].u=u;edge[em].v=v; edge[em].next=head[u]; head[u]=em++; } void tree_dp(int u,int fa) { // cout<<u<<" "<<fa<<endl; sum[u]=1; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].v; if(v==fa) continue; tree_dp(v,u); sum[u]+=sum[v]; } dp[u][sum[u]]=0; dp[u][1]=chu[u]; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].v; if(v==fa) continue; for(int j=sum[u];j>=2;j--) for(int k=1;k<=sum[v];k++) if(j>=k &&(dp[u][j-k]!=inf) &&(dp[v][k]!=inf)) dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-1);//cout<<u<<" "<<j<<" u,j "<<dp[u][j]<<" "<<u<<" "<<j-k<<" u,j-k "<<dp[u][j-k]<<" "<<v<<" "<<k<<" v,k "<<dp[v][k]<<endl; } } int main() { int p,a,b; // freopen("112.txt","r",stdin); while(scanf("%d%d",&n,&p)!=EOF) { init(); for(int i=1;i<=n-1;i++) scanf("%d%d",&a,&b),addedge(a,b),du[b]++,chu[a]++; int root; for(int i=1;i<=n;i++) if(du[i]==0) root=i; tree_dp(root,0); int mi=inf; for(int i=1;i<=n;i++) { if(i==root) mi=min(dp[i][p],mi); else mi=min(dp[i][p]+1,mi); }//如果不是根節點要要刪除連線父親的邊。 printf("%d\n",mi); } return 0; }