消防局的设立

题目地址

一道可以用贪心做的树形DP题。

对于深度最深的点,要使它被覆盖到,可以在它的兄弟、父亲、爷爷处设立消防局,而在它的爷爷设置消防局最优因为这样可以管到爷爷的爷爷

每设立一个消防局,又要把它能管到的地方都标记一遍。所以要打两个标记。一个标记记录它是否被任何一个消防局管到过(vis)。一个标记(book)记录它能否被当前设立的这一个消防局管到,这个标记要在设立下一个消防局时清零。
原因:(book)是防止父亲和儿子之间来回搜(加的双向边嘛)。如果遇到一个点的(book)为1,就会(continue),但如果这个(book)是被之前设立的消防局标记的,就会出现错误。

举个例子:
enter image description here

假如第一次取出最深的点1,相当于在右边的蓝色点设立消防局,那么所有红色的点的(book)都被标记过了。如果不清0的话,第二次选到2号点,相当于在左边的蓝色点设立消防局,它应该将所有标箭头的点都标记,但以为上一次标记的(book)没有清空,当搜到最上面的红色的点时,就会返回,所以橙色的点并没有被标记到(它应该被标记)。那么下次又会取出橙色的点。很明显答案错误。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10005;
int n,head[N],tot,ans;
struct edge{
	int next,node;
}e[20005];
struct nod{
	int dep,p;
}a[N];
bool vis[N],book[N];
void add(int x,int y)
{
	e[++tot].node=y;
	e[tot].next =head[x];
	head[x]=tot;
}
bool cmp(nod a,nod b)
{
	return a.dep >b.dep;
}
void build(int u,int fa)
{
	a[u].p=u; a[u].dep=a[fa].dep+1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].node;
		if(v==fa) continue;
		build(v,u);
	}
}
void dfs(int u,int sum)
{
	book[u]=vis[u]=1;
	if(!sum) { book[u]=0; return;}
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].node;
		if(book[v]) continue; 
		dfs(v,sum-1);
	}
	book[u]=0;
}
/*void dfs(int x,int size)
{
    vis[x]=book[x]=1;
    for(int i=head[x];i;i=e[i].next)
    {
        int to=e[i].node;
        if(!book[to]&&size>0)
         dfs(to,size-1); 
    }
    book[x]=0;
}*/
int main()
{
	scanf("%d",&n);
	int x;
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&x);
		add(i,x); add(x,i);
	}
	build(1,0);
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	if(!vis[a[i].p])
	 ++ans,dfs(a[i].p,4);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/karryW/p/11478624.html