zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",ans);
        return 0;
    }
  • 相关阅读:
    js添加和删除class
    GIT回滚master分支到指定tag版本
    table添加正确的样式
    iframe父页面与子页面赋值
    关于日期转换
    vue-cli脚手架安装
    npm手册
    linear-gradient常用实现效果
    【转载】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例
    雷霄骅走了
  • 原文地址:https://www.cnblogs.com/cglongge/p/10531035.html
Copyright © 2011-2022 走看看