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

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

ret sam string 文件 正整數 並且 關鍵字 for n-1

Description

2020年,人類在火星上建立了一個龐大的基地群,總共有n個基地。起初為了節約材料,人類只修建了n-1條道路來連接這些基地,並且每兩個基地都能夠通過道路到達,所以所有的基地形成了一個巨大的樹狀結構。如果基地A到基地B至少要經過d條道路的話,我們稱基地A到基地B的距離為d。

由於火星上非常幹燥,經常引發火災,人類決定在火星上修建若幹個消防局。消防局只能修建在基地裏,每個消防局有能力撲滅與它距離不超過2的基地的火災。

你的任務是計算至少要修建多少個消防局才能夠確保火星上所有的基地在發生火災時,消防隊有能力及時撲滅火災。

Input

輸入文件名為input.txt。

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

Output

輸出文件名為output.txt

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

Sample

輸入樣例#1:
6
1
2
3
4
5
輸出樣例#1:
2


Solution
方法1:貪心
轉化為一棵以1為根節點的有根樹,考慮深度最大的節點,當覆蓋它的節點為它的父節點的父節點時,是最優的(反證法)
技術分享

舉個栗子,考慮節點5時,1,3,4都可以覆蓋它,但是3,4能覆蓋的節點1都可以覆蓋(因為3,4的深度小一點,覆蓋的點的深度也小一點,但是5是深度最大的點,沒有深度更大的點了)

所以可以以深度為關鍵字排序,每次找到深度最大的節點,從這個節點出發走4個距離的點都標為已覆蓋
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define nn 1011
using namespace std;
int vis[nn],dis[nn],fir[nn],nxt[nn<<1],to[nn<<1],e=0;
struct node{
	int th,dis;
	bool operator <(const node &v) const{
		return dis<v.dis;
	}
}o;
priority_queue<node> q;
int read()
{
	int ans=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch==‘-‘) f=-1;ch=getchar();}
	while(isdigit(ch)) {ans=ans*10+ch-‘0‘;ch=getchar();}
	return ans*f;
}
void add(int u,int v)
{
	nxt[++e]=fir[u];fir[u]=e;to[e]=v;
	nxt[++e]=fir[v];fir[v]=e;to[e]=u;
}
void solve(int x,int fa,int use)
{
	vis[x]=1;
	if(use>=4)
	  return;
	for(int i=fir[x];i;i=nxt[i])
	  if(to[i]!=fa)
	    solve(to[i],x,use+1);
}
int main()
{
	int n,fa,ans=0;
	n=read();
	o.dis=0;o.th=1;       ////
	q.push(o);
	for(int i=2;i<=n;i++)
	{
		fa=read();
		dis[i]=dis[fa]+1;
		o.dis=dis[i];o.th=i;
		q.push(o);
		add(i,fa);
	}
	while(!q.empty())
	{
		o=q.top();q.pop();
		if(!vis[o.th])
		{
			solve(o.th,0,0);
		    ans++;
		}
	}
	printf("%d",ans);
	return 0;
}


方法2:樹形dp

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