0
点赞
收藏
分享

微信扫一扫

poj-1947

上古神龙 2023-05-23 阅读 101


//540K  32MS    G++
#include <cstdio>
#include <cstring>

using namespace std;

const int MAX = 155;

int DPRemoveMin[MAX][MAX];

int DPRemoveMinRangeFrom[MAX][MAX];

int childListHead[MAX];

struct Node {
    int parentNodeId;
    int prevBroId;
    int nextBroId;
};

typedef struct Node Node;
Node treeNodes[MAX];

int BarnNum;
int P;

const int INF = 99999999;

void insert(int parentId, int childId) {
    treeNodes[childId].parentNodeId = parentId;
    treeNodes[childId].nextBroId = childListHead[parentId];
    treeNodes[childListHead[parentId]].prevBroId = childId;
    childListHead[parentId] = childId;
}

int MIN(int a, int b) {
    return a < b ? a : b;
}

void DFS(int curNodeId) {
        //leaf node
    if (childListHead[curNodeId] == 0) {
            DPRemoveMin[curNodeId][1] = 0;
            int prevBroId = treeNodes[curNodeId].prevBroId;
            if (prevBroId == 0) {
                DPRemoveMinRangeFrom[curNodeId][1] = 0;
                // printf("R1 %d %d %d \n", curNodeId, 1, DPRemoveMinRangeFrom[curNodeId][1]);
            } else {
                for (int subtreeSizeBefore = 1; subtreeSizeBefore <= P; ++subtreeSizeBefore) {
                    for (int i = 1; i <= subtreeSizeBefore; i++) {               
                        DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore]
                            = MIN(DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore], 
                            DPRemoveMinRangeFrom[prevBroId][i] + DPRemoveMin[prevBroId][subtreeSizeBefore - i]);
                    }
                    // printf("R3 %d %d %d \n",
                    //     curNodeId, subtreeSizeBefore, DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore]);
                }
            }
            // printf("%d %d %d\n", curNodeId, 1, DPRemoveMin[curNodeId][1]);
    } else {
        // first get all child's DP
        int childId = childListHead[curNodeId];
        for ( ;childId != 0; childId = treeNodes[childId].nextBroId) {
            DFS(childId);
        }

        int prevBroId = treeNodes[curNodeId].prevBroId;
        for (int subtreeSizeBefore = 1; subtreeSizeBefore <= P; ++subtreeSizeBefore) {
            if (prevBroId) {
                // i: how many node provided by previous childNode and root node            
                for (int i = 1; i <= subtreeSizeBefore; i++) {               
                    DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore]
                        = MIN(DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore], 
                        DPRemoveMinRangeFrom[prevBroId][i] + DPRemoveMin[prevBroId][subtreeSizeBefore - i]);
                }
            } else {
                // curNode is the 1st child node.
                if (subtreeSizeBefore == 1) {                
                    DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore]
                     = 0;
                }
            }
            // printf("R %d %d %d \n", curNodeId, subtreeSizeBefore, DPRemoveMinRangeFrom[curNodeId][subtreeSizeBefore]);
        }

        // how many tree nodes are left in subtree 
        for (int subtreeSize = 1; subtreeSize <= P; ++subtreeSize) {

            int childId = childListHead[curNodeId];
            for ( ;childId != 0; childId = treeNodes[childId].nextBroId) {

                if (treeNodes[childId].nextBroId == 0) {                    
                    for (int Size1 = 1; Size1 <= subtreeSize; Size1++) {              
                        DPRemoveMin[curNodeId][subtreeSize] =
                        MIN(DPRemoveMin[curNodeId][subtreeSize], 
                            DPRemoveMinRangeFrom[childId][Size1]
                            + DPRemoveMin[childId][subtreeSize - Size1]);
                    }
                }
            }            
            // printf("%d %d %d\n", curNodeId, subtreeSize, DPRemoveMin[curNodeId][subtreeSize]);
        }
    }
}

int main() {
    while(scanf("%d %d", &BarnNum, &P) != EOF) {
        memset(treeNodes, 0, sizeof(treeNodes));
        for (int i = 0; i < MAX; i++) {
            for (int j = 0; j < MAX; j++) {
                DPRemoveMin[i][j] = INF;       
                DPRemoveMinRangeFrom[i][j] = INF;       
            }
        }
        // memset(DPRemoveMin, -1, sizeof(DPRemoveMin));
        // memset(DPRemoveMinRangeFrom, -1, sizeof(DPRemoveMinRangeFrom));
        memset(childListHead, 0, sizeof(childListHead));
        for (int i = 1; i <= BarnNum -1; i++) {
            int parentId;
            int childId;
            scanf("%d %d", &parentId, &childId);
            insert(parentId, childId);
        }
        int rootNodeId = 0;
        for (int i = 1; i <= BarnNum; i++) {
            if (treeNodes[i].parentNodeId == 0) {
                rootNodeId = i;
                // printf("rootNodeId %d\n", rootNodeId);
                break;
            }
        }
        for (int i = 1; i <= BarnNum; i++) {
            DPRemoveMin[i][0] = 1;
        }
        DFS(rootNodeId);
        int res = INF;
        for (int i = 1; i <= BarnNum; i++) {
            if (i == rootNodeId) {
                res = MIN(res, DPRemoveMin[i][P]);
            } else {
                res = MIN(res, DPRemoveMin[i][P] + 1);    
            }
        }
        printf("%d\n", res);
    }
}

巨纠结的一题, 看了很多网上的解答,很多其实解答应该是错误或者说不严谨的,起码我想不通,

我觉得下面这个应该是严谨的, 按照DFS每层是一个背包问题求解

这道题我觉得真不算是简单DP,想通了,每个变量含义明确了,确实是很清晰的,但是真心觉得是称不上简单的,起码按照这种严谨思路考虑的话.

不过这道题也真的是很经典,等于把树状DP和背包DP结合在一起了,树状DP的每层都要使用背包DP才能得到相应的解.

其实就是对于树的某个点来说,已经知道了它每个子节点保留多少个节点最少需要个多少刀,现在要用这些信息求出该节点保留多少节点最少需要多少刀,其实就是一个背包的问题.

举报

相关推荐

ZCMU—1947

poj 1691

POJ 3123

poj 2472

北大POJ

POJ 2503

0 条评论