題目描述:
Farmer John決定爲他的所有奶牛都配備手機,以此鼓勵她們互相交流。
不過,爲此FJ必須在奶牛們居住的N(1 <= N <= 10,000)塊草地中選一些建上
無線電通訊塔,來保證任意兩塊草地間都存在手機信號。所有的N塊草地按1..N
順次編號。
所有草地中只有N-1對是相鄰的,不過對任意兩塊草地A和B(1 <= A <= N;
1 <= B <= N; A != B),都可以找到一個以A開頭以B結尾的草地序列,並且序列
中相鄰的編號所代表的草地相鄰。無線電通訊塔只能建在草地上,一座塔的服務
範圍爲它所在的那塊草地,以及與那塊草地相鄰的所有草地。
請你幫FJ計算一下,爲了建立能覆蓋到所有草地的通信系統,他最少要建
多少座無線電通訊塔。
輸入:
輸入格式:
* 第1行: 1個整數,N
* 第2..N行: 每行爲2個用空格隔開的整數A、B,爲兩塊相鄰草地的編號
輸出:
輸出格式:
* 第1行: 輸出1個整數,即FJ最少建立無線電通訊塔的數目
輸入樣例:
5
1 3
5 2
4 3
3 5
輸出樣例:
2
提示:
輸入說明:
Farmer John的農場中有5塊草地:草地1和草地3相鄰,草地5和草地2、草地
4和草地3,草地3和草地5也是如此。
輸出說明:
FJ可以選擇在草地2和草地3,或是草地3和草地5上建通訊塔
思路分析:
爲了更好的理解題目,我們先將樣例的樹所畫出。
我們可以這樣想:
每一個節點一共分爲三種情況,就是f[i][0]~f[i][2]。
一是第i號節點和它的子樹都被覆蓋,但是i上無塔。
二是第i號節點和它的子樹都被覆蓋,i上有塔。
三是它的子樹都被覆蓋。
我們分別用f[i][0]表示第一種情況,f[i][1]表示第二種情況,f[i][2]表示第三種情況。
狀態定義完畢,現在我們開始思考狀態轉移。
那麼第一種,因爲i上無塔,所以就從它的兒子上取最小值轉移(因爲有多個,所以是累加)。
那麼第三種,因爲i的子樹被覆蓋,所以就是從它的兒子上取第二種情況(f[i][1])
(因爲有多個,所以是累加)。
最後是第二種,就是它與兒子的第一種情況(因爲i上有塔)與覆蓋子樹的最小費用減去子節點的第一和第二種情況的最小值的最小值。
是不是感覺蒙了,結合代碼理解吧。
代碼實現:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
vector<int>G[10005];
int n,m,f[10005][5];
void dfs(int x,int fa)
{
f[x][0]=1;
f[x][1]=10000000;
int sum=0;
for(int i=0;i<G[x].size();i++)
{
if(fa==G[x][i])
continue;
dfs(G[x][i],x);
f[x][0]+=min(f[G[x][i]][0],min(f[G[x][i]][1],f[G[x][i]][2]));
sum+=min(f[G[x][i]][1],f[G[x][i]][0]);
f[x][2]+=f[G[x][i]][1];
}
if(G[x].size()==1&&x!=1)
return;
for(int i=0;i<G[x].size();i++)
{
if(G[x][i]==fa)
continue;
f[x][1]=min(f[x][1],f[G[x][i]][0]+sum-min(f[G[x][i]][0],f[G[x][i]][1]));
}
}
int main()
{
scanf("%d",&n);
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
dfs(1,0);
printf("%d",min(f[1][1],f[1][0]));
}