1. 程式人生 > >洛谷 P2279 [HNOI2003]消防局的設立

洛谷 P2279 [HNOI2003]消防局的設立

題目描述

2020年,人類在火星上建立了一個龐大的基地群,總共有n個基地。起初為了節約材料,人類只修建了n-1條道路來連線這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了一個巨大的樹狀結構。如果基地A到基地B至少要經過d條道路的話,我們稱基地A到基地B的距離為d。
由於火星上非常乾燥,經常引發火災,人類決定在火星上修建若干個消防局。消防局只能修建在基地裡,每個消防局有能力撲滅與它距離不超過2的基地的火災。
你的任務是計算至少要修建多少個消防局才能夠確保火星上所有的基地在發生火災時,消防隊有能力及時撲滅火災。

輸入輸出格式

輸入格式:

輸入檔名為input.txt。
輸入檔案的第一行為n (n<=1000),表示火星上基地的數目。接下來的n-1行每行有一個正整數,其中檔案第i行的正整數為a[i],表示從編號為i的基地到編號為a[i]的基地之間有一條道路,為了更加簡潔的描述樹狀結構的基地群,有a[i]

輸出格式:

輸出檔名為output.txt
輸出檔案僅有一個正整數,表示至少要設立多少個消防局才有能力及時撲滅任何基地發生的火災。

輸入輸出樣例

輸入樣例#1:

6
1
2
3
4
5

輸出樣例#1:

2

分析

若要使點全部被覆蓋,那麼那些深度最大的節點是一定被覆蓋到的,這個就是我們入手點。先將有根樹轉成無根樹,那麼接下來我們考慮深度最大的點,一般的,對於深度最大的結點u,選擇u的k級祖先是最划算的(意思是說這個題目的2改成了k我們都是可以做的,至於這個結論,正確性顯然,有了這個結論後,我們就可以順理成章地開始貪心啦!(從深度最大的開始貪心,這樣防止漏解和錯解),其餘的基本操作詳見程式碼。(特別提醒!如果節點深度<=k,那麼就直接選擇整個樹的root,所以有個fa[1]=1)

程式碼

#include <bits/stdc++.h>

#define N 5005

struct NOTE
{
    int id,dep;
}t[N];

struct EDGE
{
    int to,next;
}e[N];

int n,ans=0;

int dep[N];
int head[N],cnt=0;
int father[N];
bool vis[N];

void add(int x,int y)
{
    e[++cnt].to=y;e[cnt].next=head[x];head[x]=cnt;
    e[++cnt].to=x;e[cnt].next=head[y];head[y]=cnt;
} // end void
void dfs(int v,int f) { for (int i = head[v]; i; i = e[i].next) { int now = e[i].to; if (now!=f) { dep[now] = dep[v] + 1; father[now] = v; dfs(now,v); } // end if } // end for } // end void bool cmp(NOTE a,NOTE b) { return a.dep > b.dep; } void getDown(int x,int f,int k) { if (!k) return ; for (int i = head[x]; i; i = e[i].next) { int now = e[i].to; if (now!=f) { vis[now] = true; getDown(now,x,k-1); } // end if } // end for } int main() { scanf ("%d",&n); for (int i = 2; i <= n; i++) { int x; scanf ("%d",&x); add(x,i); } // end for dep[1]=0; dfs(1,0); for (int i = 1; i <= n; i++) { t[i].id=i; t[i].dep=dep[i]; } // end for std::sort (t+1,t+n+1,cmp); father[1]=1; for (int i = 1; i <= n; i++) { int now = t[i].id; if (!vis[now]) { int g=father[father[now]]; vis[g] = true; getDown(g,0,2); ans++; } // end if } // end for printf("%d\n",ans); } // end main