【题解】洛谷P2279 消防局的设立(dfs/贪心)

这道洛谷题目可以通过贪心策略而非动态规划来解决。构建树形结构,使用深度优先搜索(DFS)计算每个叶子节点的深度。选择深度最大的节点,标记其祖父节点,然后使用特定搜索函数标记所有距离为2的节点。重复此过程直至所有节点被标记。实现时需考虑前向星链状结构存图和特殊情况处理。

这道题虽然在洛谷上归类为动态规划(树形dp),然而思考后我们可以用贪心来解决这个问题。

由于我们得到的是一个树形结构,所以我们可以模拟出这么一棵树来,然后通过dfs求出树每一个叶子结点的深度(根节点深度最小为1)。注意用dfs求深度要记录下结点的父亲结点,后面有用。因为消防局可以扑灭和它所在城市距离为2的城市的火灾,所以我们贪心,将求出的最深的节点找到,然后通过fa数组寻找其爷爷结点,并将其标记下来。接着,我们利用search函数来把和该节点距离为2的所有节点都打上标记。然后再重复这个过程,直到所有的结点都被标记过为止。注意需要利用前向星链状结构存图。里面的head[x]指的是x与其他子节点连边的tot,nnext[i]指的是将这个tot跳到另一个tot上去,to[i]就是to的tot那条边得到和x相连的结点。最后为了防止只剩下1结点没有被染色,我们应当让depth[i]>=depth[pd]。还要处理有爷爷结点和没有爷爷节点的情况。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iomanip>
using namespace std;
const int maxn=1000+5;
int n;
int depth[maxn],head[maxn],to[maxn*2],nnext[maxn*2],fa[maxn];
int b[maxn],team[maxn];
int tot;
int s=0,t=0;
int ans=0;
void add(int x,int y)
{
	tot++;
	nnext[tot]=head[x];
	head[x]=tot;
	to[tot]=y;
}
void search(int x)
{
	for(int i=head[x];i;i=nnext[i])
	{
		int now=to[i];
		b[now]=true;
		for(int j=head[now];j;j=nnext[j])
		{
			b[to[j]]=true;
		}
	}
}
void dfs(int x)
{
	for(int i=head[x];i;i=nnext[i])
	{
		int y=to[i];
		if(y==fa[x]) continue;
		fa[y]=x;
		depth[y]=depth[x]+1;
		dfs(y);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)
	{
		int q;
		cin>>q;
		add(i+1,q);
		add(q,i+1);
	}
	depth[1]=1;
	dfs(1);
/*	team[t]=1;
	t++;
	b[1]=true;*/
	
/*	while(s!=t)
	{
		int now=team[s];
		s++;
		for(int i=head[now];i;i=nnext[i])
		{
			int x=to[i];
			if(b[x]==false)
			{
				fa[x]=now;
				depth[x]=depth[now]+1;
				b[x]=true;
				team[t]=x;
				t++;
			}
		}
	}*/
//	int pd=1;
//	memset(b,false,sizeof(b));
	while(true)
	{
		bool ok=false;
		int pd=1;
		for(int i=1;i<=n;i++)
		{
			if(depth[i]>=depth[pd]&&b[i]==false)
			{
				pd=i;
				ok=true;
			}
		}
		if(ok==false)
		{
			break;
		}
		if(fa[fa[pd]]!=0)
		{
			search(fa[fa[pd]]);
		}
		else if(fa[pd]!=0)
		{
			search(fa[pd]);
		}
		else search(pd);
		ans++;
	}
	/*
	memset(b,false,sizeof(b));
	for(int j=1;j<=n;j++)
	{
		if(depth[j]>md&&b[j]==false)
		{
			md=depth[j];
			pd=j;
			for(int i=1;i<=n;i++)
			{
				if(depth[i]==depth[pd]-2&&fa[fa[pd]]==i&&b[i]==false)
				{
					ans++;
					b[i]=true;
					b[pd]=true;
					search(i);
					break;
				}
			}	
		}
	}*/
	printf("%d",ans);
	return 0;
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值