题目链接:poj1463
题目大意:
一城堡的所有的道路形成一个
n
个节点的树,如果在一个节点上放上一个士兵,那么和这个节点相连的边就会被看守住,问把所有边看守住最少需要放多少士兵。
思路:树的最小点覆盖
—
第一
步:确定
状态
—
f[x][1]
以
x
为根的子树在
x
上放置的士兵的最少所需的士兵
数目
—
f[x
][0]
以
x
为根的子树
x
上不放置的士兵的最少所需的士兵
数目
—
第二步:确定状态转移方程
—
f[x
][1]=1 +
Σ
min(f[i][
0],
f[i][
1
])
// x
上放置的士兵,于是它的儿子们可放可不放
!
—
f[x
][0]
=
Σ
f[i][
1
]
—
//
x
上不放置的士兵,它的儿子们都必须放
!
—
(i
是
x
的
儿子!!
)
—
结果为
min(f[root][0],f[root][1])
存储的话可以用邻接表存储
#include<iostream>
#include<iostream>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define maxn 1600
int n,m;
int dp[maxn][2];
struct node
{
int to,next;
}edge[maxn*2];
int head[maxn];
bool vis[maxn];
int cnt;
void dfs(int u)
{
vis[u]=1;
dp[u][0]=0;
dp[u][1]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(vis[to])continue;//排除他的父节点
dfs(to);
dp[u][0]+=dp[to][1];
dp[u][1]+=min(dp[to][1],dp[to][0]);
}
}
int main()
{
while(~scanf("%d",&n))
{
memset(head,-1,sizeof head);
memset(dp,0,sizeof dp);
memset(vis,0,sizeof vis);
int a,b,c;
cnt=0;
for(int i=0;i<n;i++)
{
scanf("%d:(%d)",&a,&m);
for(int j=0;j<m;j++)
{
scanf("%d",&b);
edge[cnt].to=b;
edge[cnt].next=head[a];
head[a]=cnt;
cnt++;
edge[cnt].to=a;
edge[cnt].next=head[b];
head[b]=cnt;
cnt++;
}
}
dfs(0);
printf("%d\n",min(dp[0][0],dp[0][1]));
}
return 0;
}