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

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
原文地址:https://www.cnblogs.com/charlotte-o/p/7637772.html