zoukankan      html  css  js  c++  java
  • POJ 1947 树形DP

    题意:输入N,P,求一棵N个节点的树最多砍多少次边,能得到一个节点数为P的树。

    这个题目还真是一时间考住我了,我怎么都没想通怎么DP,甚至后来没办法去找博客看了一下,还是觉得无法理解。今天上午,翘着水课来刷题,总算是弄懂了。

    用dp[rt][j]表示在以rt为根节点的树上,要保留j个节点,(每次j从p递减到1)需要的最小割边数,首先我们来考虑边界,如果是叶子,则dp[rt][1]=0是显而易见的,除此外,它无法构成任何其他数目节点的树,所以dp[rt][i](i为除1外的数)全部置为INF,以此来表示不存在。

    dp[rt][j]=min(dp[rt][j]+1,dp[rt][j-k]+dp[nx][k])

    即,每次遍历一个子树,如果割掉这个子树,就直接+1(之前我一度以为是要先减去该子树的节点数目后再+1,但其实j就是节点数,你管它能不能形成,不能形成说明本身当前节点数还不够,反正是INF,不影响)。如果不割掉,就把j进行分流,看看分多少个节点给nx最佳

    发现这类题目,就是01背包的翻版,每次访问子树,都把j从p到1遍历一下,如果根本总体还不够这么多节点,就是INF,反正无影响,如果达到了这个节点量,说明在此子树遍历前,已经有一个暂时的最优解,就进行节点分流,找出当前最优解。。。强调一点,dp[rt][0]必须设置为INF,因为当j=1时,给子树的分流节点没有,但是此时必须割掉子树才能达到dp[rt][1]的效果。。总之,凡是不存在的状态,均用INF表示

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    using namespace std;
    int dp[200][200];
    
    int vis[200];
    vector<int> v[200];
    int n,p;
    void clean()
    {
        for (int i=0;i<=n+5;i++){
            v[i].clear();
        }
    }
    void dfs(int rt)
    {
        vis[rt]=1;
        int r=v[rt].size();
    
        for (int i=0;i<r;i++){
            int nx=v[rt][i];
            if (vis[nx]) continue;
            dfs(nx);
    
            for (int j=p;j>=1;j--){
                int temp=dp[rt][j]+1;
                for (int k=0;k<j;k++){
                    temp=min(temp,dp[rt][j-k]+dp[nx][k]);
                }
                dp[rt][j]=temp;
            }
        }
    }
    int main()
    {
        while (scanf("%d%d",&n,&p)!=EOF)
        {
            clean();
            int i,j;
            for (i=1;i<n;i++){
                int a,b;
                scanf("%d%d",&a,&b);
                v[a].push_back(b);
                v[b].push_back(a);
            }
            memset(vis,0,sizeof vis);
            for (i=0;i<=n+5;i++){
                for (j=0;j<=n+5;j++){
                    dp[i][j]=10000000;
                }
                dp[i][1]=0;
            }
            dfs(1);
            int ans=dp[1][p];
            for (i=2;i<=n;i++){
                ans=min(ans,dp[i][p]+1);//如果最优解不是在节点1取到,意味着在最优解的那个点x肯定把1-x给切掉了,故需要+1
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    残缺的字符串
    [BZOJ3513: [MUTC2013]idiots]
    FFT感性瞎扯
    Quartz框架简介
    异常状态码总结
    【SSM】拦截器的原理、实现
    FastDFS实现文件上传下载实战
    分布式文件系统FastDFS设计原理(转)
    FastDFS简介
    【设计模式】观察者模式
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3406166.html
Copyright © 2011-2022 走看看