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

来源:互联网 发布:编程有哪些方向 编辑:程序博客网 时间:2024/05/01 14:31

移动信号

题目描述

给出一个树,有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 100020using namespace std;vector<int> f[Maxn];intdp[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;}


上面很详细了,代码就不写注释了。
0 0
原创粉丝点击