电子眼【树形dp】

题目大意:

题目链接:http://10.156.17.250/JudgeOnline/showproblem?problem_id=2119 (学校局域网)
在一棵树中选择一些点使得每条边至少与一个选择的点相邻。


思路:

很显然的树形dpdp
f[x][1/0]f[x][1/0]表示以节点xx为根,选或不选这个点的最少选择数量。
显然如果不选择这个点,那么这个点的所有儿子节点都必须选,否则这个点的子节点选择或不选都可以。
方程如下
f[x][0]=f[y][1]   (yson[x])f[x][0]=\sum f[y][1]\ \ \ (y\in son[x])
f[x][1]=(min(f[y][0],f[y][1]))+1   (yson[x])f[x][1]=(\sum min(f[y][0],f[y][1]))+1\ \ \ (y\in son[x])

特别的,当n=1n=1时直接输出1就可以了。


代码:

#include <cstdio>
#include <iostream>
using namespace std;

const int N=100010;
int n,x,y,tot,in[N],head[N],f[N][2];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dp(int x,int fa)
{
	f[x][1]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int y=e[i].to;
		if (y==fa) continue;
		dp(y,x);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	if (n==1) return !printf("1");
	for (int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
		in[x]++;
		in[y]++;
	}
	dp(1,0);
	printf("%d",min(f[1][0],f[1][1]));
	return 0;
}
posted @ 2019-03-28 16:45  全OI最菜  阅读(116)  评论(0编辑  收藏  举报