您的位置:首页 > 其它

集训-移动信号(树形DP)

2017-04-17 09:04 246 查看


移动信号

题目描述

给出一个树,有N个结点,结点编号从1至N。假如在第i个结点建立一个信号塔,那么与第i个结点有边相连的结点就能接受到信号,当然第i个结点本身也能接受到信号。

问题是:至少要在多少个结点建立信号塔,才能使得所有的结点都能接收到信息。

输入格式 1783.in

第一行,一个整数N。1 ≤ N ≤ 10,000

接下来有N-1行,每行两个整数:a b,表示结点a和结点b有边相连。1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B

输出格式 1783.out

一个整数。

输入样例 1783.in

5

1 3

5 2

4 3

3 5
输出样例 1783.out

2

用f[root][0]表示第i个点依赖儿子得到信号 且 以root为根的子树都得到信号 所建的最少信号塔个数。

用f[root][1]表示第i个点依赖自己得到信号 且 以root为根的子树都得到信号 所建的最少信号塔个数。

用f[root][2]表示第i个点依赖父亲得到信号 且 以root为根的子树都得到信号 所建的最少信号塔个数。

dp[root][1]+=min(dp[v][0],min(dp[v][1],dp[v][2]));//dp[root][1],儿子不管怎样都可以。dp[root][1]一开始为1

dp[root][2]+=min(dp[v][0],dp[v][1]);//dp[root][2],依赖父亲,儿子可以依赖自己,也可以依赖他的儿子。

dp[root][0]是最难处理的了,但跟依赖父亲的情况差不多,必须要有一个儿子是建塔的。

if(!bo)dp[root][0]=dp[root][2]+mi;(bo表示是否dp[root][2]取得都是儿子不建塔的情况,mi是min(dp[v][1]-dp[v][0]))。

边界:

叶子节点: dp[leaf][1]=1;dp[leaf][0]=INF(叶子节点不可以靠他的儿子)

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#define INF 0x3fffffff
#define Maxn 100020
using namespace std;
vector<int> f[Maxn];
int	dp[Maxn][4];
void dfs(int root,int pre)
{
int si=f[root].size();
bool bo=false;
int mi=INF;
for(int i=0;i<si;i++)if(f[root][i]!=pre)
{
int v=f[root][i];
dfs(v,root);
dp[root][1]+=min(dp[v][0],min(dp[v][1],dp[v][2]));
dp[root][2]+=min(dp[v][0],dp[v][1]);
if(dp[v][1]<dp[v][0])bo=true;
mi=min(mi,dp[v][1]-dp[v][0]);
}
if(!bo)dp[root][0]=dp[root][2]+mi;
else dp[root][0]=dp[root][2];
dp[root][1]++;
if(dp[root][0]==0)dp[root][0]=INF;
}
int main()
{
freopen("1783.in","r",stdin);
freopen("1783.out","w",stdout);
int n;
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
f[x].push_back(y);
f[y].push_back(x);
}
dfs(1,0);
printf("%d\n",min(dp[1][0],dp[1][1]));
return 0;
}


上面很详细了,代码就不写注释了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树形DP