链接:点击打开链接
题意:给出一个树,要求最少删除多少条边使叶子节点恰好平分
代码:
#include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; const int INF=0x3f3f3f3f; int n,p; int dp[5005][5005],num[5005],used[5005]; //dp[i][j]为以i为根节点的子树将j个节点分出 vector<int> G[5005]; //去删除的边的数目 void dfs1(int s){ int i,tmp,sum; sum=0; used[s]=1; num[s]=(G[s].size()==1); for(i=0;i<G[s].size();i++){ tmp=G[s][i]; if(G[tmp].size()&&!used[tmp]){ sum++; dfs1(tmp); num[s]+=num[tmp]; } } //叶子节点数 // dp[s][1]=sum; } void dfs2(int s){ int i,j,k,tmp; used[s]=1; for(i=0;i<G[s].size();i++){ tmp=G[s][i]; if(!used[tmp]){ dfs2(tmp); for(j=num[s];j>=0;j--) for(k=0;k<=min(j,num[tmp]);k++) dp[s][j]=min(dp[s][j],dp[s][j-k]+dp[tmp][k]); } } for(i=0;i<=num[s];i++) //根据s的父节点推出 dp[s][num[s]-i]=min(dp[s][num[s]-i],dp[s][i]+1); } int main(){ int i,j,a,b,ans,root; while(scanf("%d",&n)!=EOF){ memset(dp,INF,sizeof(dp)); memset(used,0,sizeof(used)); for(i=1;i<=n;i++) G[i].clear(); for(i=1;i<n;i++){ scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } if(n==2){ //两个节点没有根节点,因此直接输出1 puts("1"); continue; } root=1; while(G[root].size()==1) root++; dfs1(root); for(i=1;i<=n;i++) dp[i][0]=0; memset(used,0,sizeof(used)); dfs2(root); printf("%d\n",dp[root][num[root]/2]); } return 0; }