P2458 保安站崗/P2899 Cell Phone Network
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\)
轉移
- \(f[i][0] += min(f[to][0],f[to][1],f[to][2])\)
加入在 \(i\) 這個點放的話,那麼他的兒子可以任意放。
- \(f[i][2] += min(f[to][0],f[to][1])\)
這個時候因為 \(i\) 這個節點沒有放,所以就不能由 \(f[to][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;
}