[洛谷]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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章