树形DP
题意: 给定一棵树,选择尽量少的节点,使得每个没有选中的节点至少和一个已选节点相邻。
解法: 关于树形DP,题目通常给的是无根树,那么随便选择一个节点当做根好了,这不影响解题。题目的限制是,如果一个节点没被选中,那么和它相邻的节点至少选一个,若一个节点已选,那么和它相邻的节点选或不选都可以。那么,在树中的体现是,对于一个非根节点,他的限制来自于它的父亲(对于根我们不设限制),若它的父亲没被选,则它必须被选,若选了它的父亲,则它选或不选都可以。
设状态dp[u][0]为以u为根的子树当u不选时的最小值,dp[u][1]为选了u的最小值。那么有如下转移:
dp[u][0] = sigma(dp[v][1]) v为u在树中的儿子。
dp[u][1] = sigma(max(dp[v][1],dp[v][0]))。
/* **********************************************
Author : Nero
Created Time: 2013-8-26 12:19:21
Problem id : LA 2038
Problem Name: Strategic game
*********************************************** */
/*
树形DP,题目不难,但是唯一的巨大的坑是,书上描述n=0为结束标志,但是网页上的题目没有这一句,所以轻信者得RE。
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define REP(i,a,b) for(int i=(a); i<(int)(b); i++)
#define clr(a,b) memset(a,b,sizeof(a))
const int MAXN = 1510;
struct Edge {
int v,next;
}g[MAXN<<1];
int head[MAXN],tot;
int dp[MAXN][2];
int mark[MAXN];
void add_edge(int u, int v) {
g[tot].v = v;
g[tot].next = head[u];
head[u] = tot ++;
}
int dfs(int u, int fa, int x) {
if(!mark[u]) {
dp[u][0] = 0;
dp[u][1] = 1;
for(int p = head[u]; p != -1; p=g[p].next) {
int v = g[p].v;
if(v == fa) continue;
dp[u][0] += dfs(v,u,0);
dp[u][1] += dfs(v,u,1);
}
mark[u] = 1;
}
if(x == 1) return min(dp[u][0],dp[u][1]);
else return dp[u][1];
}
int main() {
int n;
while(~scanf("%d", &n)) {
clr(head, -1);
clr(mark,0);
tot = 0;
int m,a,u;
REP(i,0,n) {
scanf("%d", &u);
scanf(":(%d)", &m);
while(m--) {
scanf("%d", &a);
add_edge(u,a);
add_edge(a,u);
}
}
printf("%d\n", dfs(0,-1,1));
}
return 0;
}