这道题是DP。之前应该做过类似的题,对每个节点根据子节点的情况进行DP,但是忘了,看了解题报告才知道这个思路。
要点在于因为本题是树形结构,我们可以从根节点开始从上到下DP。对每个节点,有两种可能,cover or not:如果cover了,那么和这个节点相连的边都覆盖到了,所以子节点可以cover也可以不cover;如果不cover,那么子节点必须cover。用f标记父节点,用c标记(其中一个)子节点的话,我们有DP[f][0] = \sum_c DP[c][1]; DP[f][1] = 1 + \sum_c min(DP[c][0], DP[c][1])
其中DP[f][0]表示父节点不cover的情况,DP[f][1]表示父节点cover的情况。
thestoryofsnow | 1463 | Accepted | 292K | 360MS | C++ | 1529B |
/*
ID: thestor1
LANG: C++
TASK: poj1463
*/
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
#include <cassert>
using namespace std;
const int MAXN = 1500;
const int MAXM = 10;
// DP[i][0] means the total number of children (and grandchildren etc) nodes covered if i itself is NOT covered
// DP[i][1] means i itself is COVERED
int DP[MAXN][2];
// the number of children nodes
int nchildren[MAXN];
// children nodes
int children[MAXN][MAXM];
void DFS(int f)
{
DP[f][0] = 0, DP[f][1] = 1;
for (int i = 0; i < nchildren[f]; ++i)
{
int c = children[f][i];
DFS(c);
DP[f][0] += DP[c][1];
DP[f][1] += min(DP[c][0], DP[c][1]);
}
}
int main()
{
int N;
int father[MAXN];
while (scanf("%d", &N) > 0)
{
// initially, every node has no father (that is, root)
for (int i = 0; i < N; ++i)
{
father[i] = -1;
}
for (int i = 0; i < N; ++i)
{
int f, m;
scanf("%d:(%d)", &f, &m);
nchildren[f] = m;
for (int j = 0; j < m; ++j)
{
int c;
scanf("%d", &c);
father[c] = f;
children[f][j] = c;
}
}
// find the root who has no father node
int root = -1;
for (int i = 0; i < N; ++i)
{
if (father[i] == -1)
{
root = i;
break;
}
}
// printf("[debug]root:%d\n", root);
DFS(root);
printf("%d\n", min(DP[root][0], DP[root][1]));
}
return 0;
}