poj1947

题意:给你一棵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\n",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\n",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\n",ans);
    return 0;
}

 

posted @ 2019-03-14 16:16  cglong  阅读(262)  评论(0编辑  收藏  举报