zoukankan      html  css  js  c++  java
  • POJ1947 Rebuilding Roads 树形DP

    这题在yefeng1627的淫威下迅速屈服.  由刚开始的一个较动态方程便很好的解决了组合问题, 再加之进一步分析, 将本来应该要的辅助状态删除, 剩下的就是一个非常优美的动态规划方程了.

    详见代码:

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #define INF 0x3f3f3f3f
    using namespace std;
    
    /*
    题意:给定一棵树, 现在要求从这棵树中分割出P个节点的子树, 问最少要破坏多少条路
    
    解法:首先这个问题的复杂性主要在于如果这个分割出来的子树包含点A,那么A的子树各对这
         P个节点贡献多少呢, 这是一个组合问题, 由于节点个数达到可能100以上, 因此组合
         情况非常多, 这是状态如何选取就具有一定的技巧性. 既然组合情况很多, 那么我们
         开设的状态也同样是一个非常模糊的状态, 即只值保留最优值, 而对其实如何而来的
         并不在乎, 于是便有状态 dp[x][i][j]表示第x号节点处理到第i棵子树时分割出剩下j
         个节点的子树所需要破坏的最少路径是多少. 分割出来的节点不为零就一定包含x,那
         么就有动态方程:  dp[x][i][j] = min(dp[x][i-1][k] + dp[xi][ch[xi]][j-k]); 
         这个方程意思就是处理到i个子树的时候分割出j个节点的方式是有前i-1棵子树和当前
         这棵子树的组合, 这样就每次只考虑两个部分的组合情况, 而不必纠结多个子树时如何
         组合的. 注意边界情况;
         dp[x][0][1] = 0, 表示还没处理子树时, 当前节点是存在的
         dp[x][ch[x]][0] = 1, 表示包含x的这棵子树如果都不取的话,那么只需要切断上面的路 
         
    */
    
    int N, P, idx, ch[155], head[155];
    int ret, dp[155][155][155];
    
    struct Node {
        int v, next;
    }e[155];
    
    void insert(int a, int b) {
        ++idx;
        e[idx].v = b, e[idx].next = head[a];
        head[a] = idx;
    }
    
    void dfs(int x) {
        if (head[x] == -1) { // 该节点是叶子节点
            dp[x][0][0] = 1;
            dp[x][0][1] = 0; // WA一次因为该处直接return导致最后没有统计到这个结果
        } else {
            dp[x][0][1] = 0;
            dp[x][ch[x]][0] = 1;
            for (int i = head[x], k = 1; i != -1; i = e[i].next, ++k) { // 枚举一个处理到第k棵子树 
                dfs(e[i].v);
                for (int j = 1; j <= P; ++j) {
                    for (int m = 0; m < j; ++m) { // 由于根节点已经有1个节点, 所以只要孩子中有P-1个节点足矣 
                        dp[x][k][j] = min(dp[x][k][j], dp[x][k-1][j-m]+dp[e[i].v][ch[e[i].v]][m]);
                    }
                }
            }
        }
        if (dp[x][ch[x]][P] != INF) {
            if (x != 1) {
                ret = min(ret, dp[x][ch[x]][P]+1);
            } else {
                ret = min(ret, dp[x][ch[x]][P]);
            }
        }
    }
    
    int main() {
        int a, b;
        while (scanf("%d %d", &N, &P) == 2) {
            if (N == 1) {
                printf("0\n");
                continue;    
            }
            idx = -1;
            ret = INF;
            memset(dp, 0x3f, sizeof (int [155][155][155]));
            memset(ch, 0, sizeof (ch));
            memset(head, 0xff, sizeof (head));
            for (int i = 1; i < N; ++i) {
                scanf("%d %d", &a, &b);
                insert(a, b);
                ++ch[a]; // a节点的孩子数加1 
            }
            dfs(1);
            printf("%d\n", ret);
        }
        return 0;    
    }
  • 相关阅读:
    日本最大的汽车品牌:丰田【仅供自己参考】
    读书笔记1
    读书笔记1
    计算机网络笔记1
    ZY凉凉经
    HK凉凉经
    访问一个网站,发生了什么?
    正向代理VS反向代理
    mac下打开hosts文件
    国际手机区号
  • 原文地址:https://www.cnblogs.com/Lyush/p/2864552.html
Copyright © 2011-2022 走看看