P1751 贪吃虫 题解
题意:
- 在一棵 n 个结点的树上,有 k 个贪吃虫去吃食物。
 - 每个贪吃虫都走到达食物的唯一路径。
 - 当一条贪吃虫通向食物的道路上有另一条贪吃虫,则较远的那只停止移动。
 - 多条贪吃虫要进入同一节点时,编号最小的才能进入,其他的停止移动。
 - 贪吃虫的移动速度皆为 1。一只贪吃虫吃到食物后,另一个食物立刻出现在树上,贪吃虫继续按以上规则移动。
 - 最后求出每条贪吃虫所在的位置与吃到食物的数量。
 
思路:
可以把每一个食物看成一个测试点。那么就是要计算出每个节点被哪条贪吃虫占领和每条贪吃虫最终停留的地方。
很明显,对于这两个要计算的值,可以通过两次 dfs 来求。
第一次 dfs。对于每一个节点,占领它的贪吃虫一定是到达用时最短的那个。到达它的最短的时间就是最短的子节点到达时间 +1。还要注意两种特殊情况,在代码里也会有说明:
该点已有贪吃虫。
有速度相同,取编号最小的贪吃虫。
结束后把占领食物所在节点的贪吃虫吃到的食物数 +1。
第二次 dfs 时,可以利用第一次求出的值取计算。因为在第一次 dfs 时,我们求出了占领每个节点所用的时间,所以可以用一个数组去记录每一条贪吃虫到达最终落脚点的时间,这个值是可以算出的。如果对于某个节点,占据他的贪吃虫到达最终落脚点的时间 = 该节点被占领的时间,则该节点为这条贪吃虫的最终落脚点。
代码:
请勿抄袭。
#include<bits/stdc++.h>
using namespace std;
int n,k,h;
//n,k,h与题中含义一致
struct stu{
	int nxt,to;
}e[10010];
int cnt;
int head[10010];
//以上为链式前向星存图
int b[5001];//每一条贪吃虫所在节点
int p[5001];//每个节点上为哪条贪吃虫,0表示没有贪吃虫
int c[501];//每一次食物出现的地方
int eat[5001];//每条贪吃虫吃的食物数
int t[5001];//占领每个节点的时间
int o[5001];//每个节点被占据的贪吃虫编号
int f[5001];//题解中第二次搜索用来记录的数组
inline void add(int x,int y)//建边
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
inline void dfs1(int now,int fa)//第一次搜索,求出每个节点被哪条贪吃虫占据
//now为当前搜索到的节点,fa为上一次搜索的节点,避免重复搜索
{
	int mp,mt;//记录占领该节点的时间与贪吃虫编号
	if(p[now])//该点上已有贪吃虫
	{
		mp=p[now];//占领的贪吃虫即为该节点上的贪吃虫
		mt=0;//占领速度为0
	}
	else //否则因为要取最小值,所以设一个大的数
	{
		mp=9999;
		mt=9999;
	}
	for(int i=head[now];i;i=e[i].nxt)//依次搜索每一个儿子
	{
		int to=e[i].to;
		if(to==fa) continue;//避免重复搜索
		dfs1(to,now);
		if((t[to]+1)<mt||((t[to]+1)==mt&&o[to]<mp))
		//时间更短或时间相同并且编号较小即可更新
		{
			mt=t[to]+1;
			mp=o[to];
		}
	}
	t[now]=mt;
	o[now]=mp;
	//保存计算后的值
}
inline void dfs2(int now,int fa)//第二次搜索,计算每一只贪吃虫最终落脚点
//参数意义与dfs1一致
{
	if(o[now]!=9999)//=9999说明没有贪吃虫来过,没必要计算
	{
		if(f[o[now]]==-1&&o[fa]!=o[now])
		//没有计算过,后面的条件是因为贪吃虫一样,那么f[o[now]]没计算过,f[o[fa]]也一定没计算过
		{   //计算,取三种可能时间的最小值
			int mt=min(t[fa],t[now]);
			f[o[now]]=min(f[o[fa]],mt);
		}
		if(f[o[now]]!=-1&&f[o[now]]==t[now]) b[o[now]]=now;//计算过且时间一致,则该点为这条贪吃虫的最终落脚点
	}
	for(int i=head[now];i;i=e[i].nxt)//继续搜索子节点
	{
		int to=e[i].to;
		if(to==fa) continue;//同理,防重
		dfs2(to,now);
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)//读入这棵树,并建树(边)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);//建双向边
	}
	scanf("%d",&k);
	for(int i=1;i<=k;i++)//记录每一条贪吃虫的位置
	{
		int x;
		scanf("%d",&x);
		p[x]=i;
		b[i]=x;//按照数组含义记录
	}
	scanf("%d",&h);
	for(int i=1;i<=h;i++)//读入每一次食物出现的位置
	{
		scanf("%d",&c[i]);
	}
	for(int i=1;i<=h;i++)
	{
		memset(t,0,sizeof(t));
		memset(o,0,sizeof(o));
		memset(f,-1,sizeof(f));//由于数组内还有上一次循环的值,因此将其初始化
		dfs1(c[i],-1);//搜索
		++eat[o[c[i]]];//将吃到食物的贪吃虫更新
		f[o[c[i]]]=t[c[i]];//吃到食物的贪吃虫最终落脚点即为食物处
						   //因此到达最终落脚点的位置即为到达食物的位置
						   //这里也是为了下面的搜索的计算
		dfs2(c[i],-1);
		memset(p,0,sizeof(p));//由于这一轮过后贪吃虫又有了新的位置,因此先将旧的清零,并更新
		for(int j=1;j<=k;j++)
		{
			p[b[j]]=j;
		}
	}
	for(int i=1;i<=k;i++)//输出答案
	{
		printf("%d %d\n",b[i],eat[i]);
	}
	return 0;
}
写题解不易,点个赞呗。
P1751 贪吃虫 题解的更多相关文章
- 游戏:贪吃虫(GreedyMaggot)
		
该游戏类似于贪吃蛇,但可以在二维平面上以任意方向前进.当吃到食物手,食物会从虫头向虫尾移动,移到虫尾后,贪吃虫长度会增加.本来给它取名为贪吃蛆的,并且工程的英文名Maggot就是蛆的意思,后来想想有点 ...
 - 洛谷P7078 [CSP-S2020] 贪吃蛇 题解
		
比赛里能做出这题的人真的非常厉害,至少他的智商和蛇一样足够聪明. 首先有一个结论: 当前最强的蛇吃了最弱的蛇之后,如果没有变成最弱的蛇,他一定会选择吃! 证明: 假设当前最强的蛇叫石老板. 如果下一条 ...
 - Hadoop企业级应用
		
Hadoop专业解决方案之构建Hadoop企业级应用 一.大数据的挑战 大数据面对挑战是你必须重新思考构建数据分析应用的方式.传统方式的应用构建是基于数据存储在不支持大数据处理的基础之上.这主要是因为 ...
 - WhyEngine游戏合集2014贺岁版
		
WhyEngine游戏合集2014贺岁版 自去年9月份开始写我的第一个小游戏,到现在为止,共实现了14个小游戏,10个屏保程序,7个DEMO程序.开发环境是VS2008,渲染使用的是D3D,所有代码都 ...
 - Why游戏作品合集
		
之前曾经发过一个套WhyEngine游戏作品合集,里面有十几个小游戏和若干个屏保程序和若干个DEMO程序.而这次发的与上次不一样,因为这是我花了两天时间将所有的程序集成到一个工程后的成果.为了能将所有 ...
 - WhyEngine游戏引擎作品合集
		
从9月份开始写三个月内总共实现了13个游戏,5个屏保程序,5个DEMO程序.如果运行时,报有木马病毒什么的,请相信我,这绝对是杀毒软件的误报,自己写的程序由于没有得到杀毒软件的认证,被报有危险是正常的 ...
 - 【NOIp2004提高组】食虫算 题解
		
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 + 8468#6633 44445509678 其中#号代表被 ...
 - 【题解】 P1092虫食算
		
[题解]P1092 虫食算 老题了,很经典. 用到了一些搜索套路. 可行性剪枝,劣者靠后,随机化,\(etc......\) 搜索设参也很有技巧,设一个\(adjustment\)参数可以很方便地在两 ...
 - 【题解】ZJOI2010贪吃的老鼠
		
%%%%真的好强...看题解我都看了好久才完全明白.放一下参考的博客,谢谢神犇QAQ 1号博客 2号博客(超级赞的啦) 因为理解的过程太艰辛,所以必须记录一下这道强题:这道题目最难的两个约束就在 ...
 - NOIP 2004 虫食算题解
		
问题 E: [Noip2004]虫食算 时间限制: 1 Sec 内存限制: 128 MB 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一 ...
 
随机推荐
- XAML 设计器已意外退出。(退出代码: e0434352)
			
一.前言 开门见山,这个问题我遇到过两次,第一次因为项目刚开始不长时间,我查了很长时间都没解决,然后就直接重写了,几乎一样的写法,但问题没复现了,但程序员思维告诉我,一定还是有比较关键的地方出现了问题 ...
 - js循环中reduce的用法简单介绍
			
reduce() 方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(上一次回调的返回值),当前元素值,当前索引 ...
 - 二进制安装Kubernetes(k8s) v1.26.1 IPv4/IPv6双栈 可脱离互联网
			
二进制安装Kubernetes(k8s) v1.26.1 IPv4/IPv6双栈 可脱离互联网 https://github.com/cby-chen/Kubernetes 开源不易,帮忙点个star ...
 - KubeSphere 高可用集群搭建并启用所有插件
			
介绍 大多数情况下,单主节点集群大致足以供开发和测试环境使用.但是,对于生产环境,您需要考虑集群的高可用性.如果关键组件(例如 kube-apiserver.kube-scheduler 和 kube ...
 - [JavaScript]Base64 ←→ 图像
			
1 Base64 → 图像 [demo1] document.getElementById('img').setAttribute( 'src', 'data:image/png;base64,iVB ...
 - [Java]排序算法>选择排序>【简单选择排序】(O(n*n)/不稳定/)
			
1 选择排序 1.1 算法思想 每一趟从待排序的记录中选出关键字最小的记录,按顺序放在已排序的记录序列的最后(or最前面),直到全部排完位置. 1.2 算法特征 属于[选择排序] 简单选择排序 堆排序 ...
 - Luogu P2574 XOR的艺术 P3870 [TJOI2009]开关 P2846 [USACO08NOV]光开关Light Switching SP7259 LITE - Light Switching
			
四倍经验题 简单线段树qwq(那你怎么还调了好几个小时) 修改:\(ans[cur]=(r-l+1-ans[cur]);\) 点表示的区间有多长就是有多少盏灯 亮着的关掉 暗的开启 就是上述语句了. ...
 - Docker中Nginx搭建以及配置
			
docker nginx搭建 1 docker pull nginx docker pull nginx 2 启动nginx docker run --name nginx -p 80:80 -d n ...
 - day01-Redis入门
			
Redis入门 1.初始Redis 1.1认识NoSQL SQL(关系型数据库) NoSQL(非关系型数据库) 数据结构 结构化(Structured) 非结构化 数据关联 关联的(Relationa ...
 - Go语言核心知识回顾(接口、Context、协程)
			
温故而知新 接口 接口是一种共享边界,计算机系统的各个独立组件可以在这个共享边界上交换信息,在面向对象的编程语言,接口指的是相互独立的两个对象之间的交流方式,接口有如下好处: 隐藏细节: 对对象进行必 ...