1. 程式人生 > 實用技巧 >P2458 保安站崗/P2899 Cell Phone Network

P2458 保安站崗/P2899 Cell Phone Network

Link Link2

desprition

五一來臨,某地下超市為了便於疏通和指揮密集的人員和車輛,以免造成超市內的混亂和擁擠,準備臨時從外單位呼叫部分保安來維持交通秩序。

已知整個地下超市的所有通道呈一棵樹的形狀;某些通道之間可以互相望見。總經理要求所有通道的每個端點(樹的頂點)都要有人全天候看守,在不同的通道端點安排保安所需的費用不同。

一個保安一旦站在某個通道的其中一個端點,那麼他除了能看守住他所站的那個端點,也能看到這個通道的另一個端點,所以一個保安可能同時能看守住多個端點(樹的結點),因此沒有必要在每個通道的端點都安排保安。

程式設計任務:

請你幫助超市經理策劃安排,在能看守全部通道端點的前提下,使得花費的經費最少。

solution

一開始自己拿戰略遊戲那道題的思路寫了這道題,然後就獲得了 \(10pts\) 的好成績。

仔細想想這兩道題之間還是有區別的:

  • 戰略遊戲是每條邊的左右端點至少選一個。
  • 但這道題不太一樣,這兩個點可以都不選,比如左端點被他的父親覆蓋,然後右端點被他兒子覆蓋。

這樣我們的狀態設計就出現了問題。

考慮到一個點只會被他自己,他兒子或者他父親覆蓋。

然後就設 \(f[i][0/1/2]\) 分別表示 在這個點放/在這個點的兒子處放/在這個點的父親處放的最小花費。

初始化: \(f[i][0] = a[i], f[i][1] = f[i][2] = 0\)

特別的當 \(i\) 為葉子節點時, \(f[i][1] = inf\)

,因為他下面沒有兒子可以覆蓋。

轉移

  1. \(f[i][0] += min(f[to][0],f[to][1],f[to][2])\)

​ 加入在 \(i\) 這個點放的話,那麼他的兒子可以任意放。

  1. \(f[i][2] += min(f[to][0],f[to][1])\)

​ 這個時候因為 \(i\) 這個節點沒有放,所以就不能由 \(f[to][1]\) 轉移過來。

  1. \(f[i][1] += min(f[to][0],f[to][1])\) 至少選一個 \(f[to][0]\)

​ 這個時候只需要滿足至少有一個兒子節點放就行,其他兒子隨便放。

​ 這個直接轉移是 \(O(n^2)\)

的,考慮優化。

​ 記錄一個差值 \(tmp = min(tmp,max(0,f[to][0]-f[to][1]))\)

​ 如果 \(tmp == 0\) 說明我們至少選了 一個 \(f[to][1]\) ,而 \(tmp\) 不為零的時候直接把差值最小的那個即 \(tmp\) 加上去就行。

最後答案 \(min(f[1][0],f[1][1])\) 因為 \(1\) 沒有父親節點,所以自然也不存在被父親覆蓋這種情況。

又水了兩道藍題呢,(◕ᴗ◕✿)。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
const int inf = 2333333;
int n,x,son,tot;
int head[N],siz[N],a[N],f[N][3];
struct node
{
	int to,net;
}e[N<<1];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
void add(int x,int y)
{
	e[++tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
void dfs(int x,int fa)
{
	f[x][0] = a[x];
	f[x][1] = f[x][2] = 0;
	int tmp = inf;
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if(to == fa) continue;
		dfs(to,x);
		f[x][0] += min(f[to][0],min(f[to][1],f[to][2]));
		f[x][1] += min(f[to][0],f[to][1]);
		f[x][2] += min(f[to][0],f[to][1]);
		tmp = min(tmp,max(0,f[to][0]-f[to][1]));
	}
	if(siz[x] == 0) f[x][1] = inf;
	else f[x][1] += tmp;
}
int main()
{
	n = read();
	for(int i = 1; i <= n; i++)
	{
		x = read(); a[x] = read(); siz[x] = read();
		for(int i = 1; i <= siz[x]; i++)
		{
			son = read();
			add(x,son); add(son,x);
		}
	}
	dfs(1,1);
	printf("%d\n",min(f[1][0],f[1][1]));
	return 0;
}