zoukankan      html  css  js  c++  java
  • Poj 1112 Rebuilding Roads(树形DP+背包)

    题意:给你由N个点构成一颗树,问要孤立出一个有P个节点的子树最少需要删除多少条边。N的范围最大为150

    N的范围不大,很容易想到在树上面做背包。把每个节点都看成一个背包,然后把每个儿子节点都看成是一组物品。为什么是一组呢,那是因为假设以儿子为根的节点的子树有S个节点,那么就有S+1种情况,要么将这整棵子树舍弃,要么从这个子树中取1-S个节点。

    设f[i][j]为以i为根节点的子树,孤立出以i为根节点,一共含有j个节点的子树最少需要删除的边数(不包括删除i和他父亲的连接的那条边(假设i不是根节点))。

    从根节点开始dfs,对于每个节点做一次分组背包,如果不要这颗子树的话分组讨论

    不要这个子树f[i][j]:f[i][j] = f[i][j]+1

    取这个子树当中的节点 f[i][j] = min(f[i][j-k],f[i.child[a]][k])

    所以总的就是f[i][j] = min(f[i][j]+1,min(f[i][j-k],f[i.child[a]][k])

    注意最后那颗子树可能不是以原来的root为根的

    #include <cstdio>
    #include <sstream>
    #include <fstream>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <map>
    #include <cctype>
    #include <ctime>
    #include <set>
    #include <climits>
    #include <vector>
    #include <queue>
    #include <stack>
    #include <cstdlib>
    #include <cmath>
    #include <string>
    #include <list>
    
    #define INPUT_FILE "in.txt"
    #define OUTPUT_FILE "out.txt"
    
    using namespace std;
    
    typedef long long LL;
    const int INF = INT_MAX / 4;
    
    void setfile() {
        freopen(INPUT_FILE,"r",stdin);
        freopen(OUTPUT_FILE,"w",stdout);
    }
    
    const int maxn = 155;
    struct Node {
        int cnt,ch[maxn],p,cc;
    };
    
    Node node[maxn];
    int N,P,dp[maxn][maxn];
    
    void dfs(int now,int sz) {
        dp[now][1] = 0;
        for(int i = 0;i < node[now].cnt;i++) {
            int chid = node[now].ch[i];
            dfs(chid,sz);
            for(int j = sz;j >= 1;j--) {
                int nret = INF;
                dp[now][j] = dp[now][j] + 1;
                for(int k = j-1;k >= 1;k--) {
                    nret = min(dp[chid][k] + dp[now][j-k],nret);
                }
                dp[now][j] = min(nret,dp[now][j]);
            }
        }
    } 
    
    int main() {
    //    freopen("in.txt","r",stdin);
        while(scanf("%d%d",&N,&P) != EOF) {
            memset(node,0,sizeof(node));
            for(int i = 0;i < N - 1;i++) {
                int a,b; scanf("%d%d",&a,&b);
                node[a].ch[node[a].cnt++] = b;
                node[b].p = a;
            }
            int root = 0;
            for(int i = 1;i <= N;i++) {
                for(int j = 1;j <= N;j++) {
                    dp[i][j] = INF;
                }
            }
            for(int i = 1;i <= N;i++) {
                if(node[i].p == 0) {
                    root = i; break;
                }
            }
            dfs(root,P);
            int ans = dp[root][P];
            for(int i = 1;i <= N;i++) {
                if(i != root) {
                    if(dp[i][P] + 1 < ans) {
                        ans = dp[i][P] + 1;
                    }
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Linux Shell中的特殊符号和含义简明总结(包含了绝大部份)
    Linux学习笔记:cat、tac、more、less、head、tail查看文件内容
    Linux学习笔记:pwd与dirs的区别
    Linux学习笔记:nohup & 后台任务
    Linux学习笔记:输入输出重定向 >>命令
    Linux学习笔记:vi常用命令
    Linux学习笔记:touch新建文件、修改访问、改动时间
    Linux学习笔记:mkdir创建文件夹
    Linux学习笔记:mv移动或文件重命名
    Shell学习笔记:#*、%*字符串掐头去尾方法
  • 原文地址:https://www.cnblogs.com/rolight/p/3737553.html
Copyright © 2011-2022 走看看