[洛谷]P2899 [USACO08JAN]手机网络 (#树形dp)

题目描述

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.

Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B) there is a sequence of adjacent pastures such that A is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.

Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

John想让他的所有牛用上手机以便相互交流(也是醉了。。。),他需要建立几座信号塔在N块草地中。已知与信号塔相邻的草地能收到信号。给你N-1个草地(A,B)的相邻关系,问:最少需要建多少个信号塔能实现所有草地都有信号。

输入格式

* Line 1: A single integer: N

* Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

输出格式

* Line 1: A single integer indicating the minimum number of towers to install

输入输出样例

输入 #1复制

5
1 3
5 2
4 3
3 5

输出 #1复制

2

思路

有一点像树的最大独立集。

首先对于每个节点i,都有如下3种选择:

0.不选自己,选儿子;

1.不选自己,选父亲;

2.我自己选我自己。

令dp[i][0/1/2]为节点i及其节点i的子树中全部被信号覆盖所需的最小信号塔数,dp[i][0]表示点i有信号塔,dp[i][1]为点i没有信号,也就是就是父亲有信号塔,dp[i][2]表示节点i被间接信号,也就是儿子有信号塔。

1.dp[i][0]

因为节点i有信息塔,所以对于i的子节点son,它可以没有信号塔,也可以有信号塔,也可以从son的子节点转移过来。因此:

dp[i][0]=∑min(dp[son][1],dp[son][0],dp[son][2])+1

这里+1是因为自己本身放置了一个信号塔。

2.dp[i][1]

因为节点i没有信息塔,靠的是节点i的父亲才有信号的,所以对于节点i的子节点son是不可能选父亲的。所以dp[i][1]可以从dp[son][0]转移过来(子节点有信息塔),也可以从dp[son][2]转移过来(子节点的儿子有信息塔)。

dp[i][1]=Σmin(dp[son][0[,dp[son][2])

3.dp[i][2]

因为节点i没有信息塔,靠它儿子,所以节点i的子节点son也不可能选父亲,必然可以从dp[son][0]转移过来,也可以从dp[son][2]转移过来。

dp[i][2]=Σmin(dp[son][0],dp[son][2])

很快会发现,如果的确是这样的话,如果dp[i][2]全从dp[son][2]转移过来,也就是若恒有dp[son][2]<=dp[son][0],就意味着节点i的所有子节点都没有信息塔!(那不就凉了)怎么办?

所以我们要设立一个反悔机制我只需要一个儿子选。只需要用p来记录每一次dp[son][0]-min(dp[son][2],dp[son][0]),这样就保证,就算恒有dp[son][2]<=dp[son][0],也一定有一个是儿子选了的,如果不选dp[son][2],最后p的状态也为0。若不信,我们做个推导:

记p=min(p,dp[son][0]-min(dp[son][0],dp[son][2]))

dp[i][2]=min(dp[son][0],dp[son][2])+p

=min(dp[son][0],dp[son][2])+dp[son][0]-min(dp[son][0],dp[son][2])

若dp[son][2]<=dp[son][0]恒成立(注意我说的是恒成立,意思是说dp[i][2]全是从dp[son][2]转移过来)

原式=dp[son][2]+dp[son][0]-dp[son][2]

=dp[son][0]

也就是把dp[son][2]强制转换成了dp[son][0]!

若dp[son][2]>dp[son][0]

原式=dp[son][0]+dp[son][0]-dp[son][0]

=dp[son][0]

也就是说并不影响dp[son][0]的正常取值!

最后答案为min(dp[root][0],dp[root][2])。

#include <stdio.h>
#include <iostream>
#define inf 2e9+7
#define maxn 300001
using namespace std;
int n,cnt,head[maxn],s,dp[maxn][3];
struct node
{
	int to,nxt;
}e[maxn<<1];
inline void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int i,int fa)
{
	bool f(0);
	register int j,p(inf);
	for(j=head[i];j;j=e[j].nxt)
	{
		int v(e[j].to);
		if(v==fa) continue;
		dfs(v,i);
		dp[i][0]+=min(dp[v][1],min(dp[v][0],dp[v][2]));
		dp[i][1]+=min(dp[v][0],dp[v][2]);
		dp[i][2]+=min(dp[v][0],dp[v][2]);
		p=min(p,dp[v][0]-min(dp[v][2],dp[v][0]));//表示其它儿子的总和
	}
	dp[i][2]+=p;//最后再进行反悔操作
	dp[i][0]++;//最后再进行放置信号塔操作 
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j;
	cin>>n;
	for(i=1;i<=n-1;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	dfs(1,-1);
	cout<<min(dp[1][0],dp[1][2])<<endl;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值