//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每层是一个背包问题求解:
http://www.cnblogs.com/rainydays/archive/2011/06/08/2075391.html
这道题我觉得真不算是简单DP,想通了,每个变量含义明确了,确实是很清晰的,但是真心觉得是称不上简单的,起码按照这种严谨思路考虑的话.
不过这道题也真的是很经典,等于把树状DP和背包DP结合在一起了,树状DP的每层都要使用背包DP才能得到相应的解.
其实就是对于树的某个点来说,已经知道了它每个子节点保留多少个节点最少需要个多少刀,现在要用这些信息求出该节点保留多少节点最少需要多少刀,其实就是一个背包的问题.