Codeforce 581F(树形dp)

链接:点击打开链接

题意:给出一个树,要求最少删除多少条边使叶子节点恰好平分

代码:

#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;
}


 

 

你可能感兴趣的:(源码,动态规划,ACM)