1. 程式人生 > 實用技巧 >[Luogu] P2899 [USACO08JAN]Cell Phone Network G

[Luogu] P2899 [USACO08JAN]Cell Phone Network G

\(Link\)

Description

John想讓他的所有牛用上手機以便相互交流,他需要建立幾座訊號塔在\(N\)塊草地中。已知與訊號塔相鄰的草地能收到訊號。給你\(N-1\)個草地\((A,B)\)的相鄰關係,問:最少需要建多少個訊號塔能實現所有草地都有訊號。

Solution

妙妙的樹形\(DP\)

顯然\(x\)只能被自己的兒子/父親染色後波及,或者自己就已經被染色。我們設\(dp[x][0/1/2]\)分別表示\(x\)是在\(x\)自己/自己的兒子/自己的父親染色後被波及(被其他點波及的,自己肯定就不用染色了),且子樹全被波及所需要的最少染色次數。設\(x\)的兒子為\(y\),再分別進行轉移:

\(1.dp[x][0]=\sum{min(dp[y][0],dp[y][1],dp[y][2])}\)\(y\)可以自己染色,被\(x\)波及,和被它的兒子波及)

\(2.dp[x][2]=\sum{min(dp[y][0],dp[y][1])}\)\(y\)可以自己染色和被它的兒子波及,但不會被\(x\)波及)

\(dp[x][1]\)的轉移比較複雜。顯然它也只會由\(dp[y][0]\)\(dp[y][1]\)轉移而來,但具體要怎麼轉移呢?

我們貪心地想,肯定是要取\(min(dp[y][0],dp[y][1])\)。但比較特殊的是,\(dp[x][1]\)也不能全由\(dp[y][1]\)

轉移來,因為它至少要有一個自己被染色的兒子來波及它。

所以我們先把\(dp[x][1]\)加上\(\sum{dp[y][0]}\),再把\(dp[y][1]-dp[y][0]\)的值存到一個佇列\(que\)裡。處理完\(x\)的所有子節點後,再來處理\(dp[x][1]\)。顯然,若\(que[i]<0\),那麼我們就把\(dp[y][0]\)替換成\(dp[y][1]\)(即\(dp[x][1]+=que[i]\))。所以直接從小到大排序,如果\(que[i]>0\)\(break\)掉。這樣也能保證替換的值是最優的。還要注意兩點:首先如果\(x\)沒有兒子,那麼\(dp[x][1]\)

要設成\(INF\)。其次設\(que[]\)\(t\)個元素,我們只能列舉到\(t-1\)個元素,原因之前已經說過了。

最後要注意每次\(dfs\)要新開一個佇列,不能用全域性變數!!(可能是因為回溯之類的奇奇怪怪的原因吧)

Code

#include <bits/stdc++.h>

using namespace std;

int n, tot, hd[100005], to[200005], nxt[200005], dp[100005][3];

int read()
{
	int x = 0, fl = 1; char ch = getchar();
	while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
	return x * fl;
}

void add(int x, int y)
{
	tot ++ ;
	to[tot] = y;
	nxt[tot] = hd[x];
	hd[x] = tot;
	return;
}

void dfs(int x, int fa)
{
	dp[x][0] = 1; int t = 0, que[100005];
	for (int i = hd[x]; i; i = nxt[i])
	{
		int y = to[i];
		if (y == fa) continue;
		dfs(y, x);
		dp[x][0] += min(dp[y][1], dp[y][2]);
		dp[x][1] += dp[y][0];
		dp[x][2] += min(dp[y][0], dp[y][1]);
		que[ ++ t] = dp[y][1] - dp[y][0];
	}
	sort(que + 1, que + t + 1);
	if (!t) dp[x][1] = 1e9;
	else
	{
		for (int i = 1; i < t; i ++ )
		{
			if (que[i] < 0) dp[x][1] += que[i];
			else break;
		}
	}
	for (int i = 1; i <= t; i ++ )
		que[i] = 0;
	return;
}

int main()
{
	n = read();
	for (int i = 1; i <= n - 1; i ++ )
	{
		int x = read(), y = read();
		add(x, y); add(y, x);
	}
	dfs(1, 0);
	printf("%d\n", min(dp[1][0], dp[1][1]));
	return 0;
}