Gym-101915 G.Robots(思维题/调整dfs顺序)

题目

有一棵n(n<=1e5)个节点的树,树的每条边都有一个权值w,1号节点为根节点。

有q个机器人,第i个机器人有自己的权值xi。

第i个机器人从根节点出发,每次只走严格比xi小的权值最大的边。

求所有机器人最终停下来的节点的标号之和。

思路来源

https://www.cnblogs.com/ZGQblogs/p/10136243.html

题解

优先队列把机器人的权值都存进去,

每次按边权最大的搜,

回溯时如果机器人的权值大于该权值,说明可以留下

留下具体分两种情况,一种是这本身就是叶子结点

另一种是这是删掉机器人不能留下的权值最大的节点后,能留下的权值最大的叶子节点

如果没有留下,其实就相当于没有来过

正如迷宫回溯当且仅当该条路行不通一样,这里机器人回溯当且仅当当前搜索路径不可留

心得

调整搜索顺序是图论中的重要技巧鸭

往往用vector便于实现,链式前向星实在是不好排序

如CF1037D的D,调整bfs顺序,使之尽可能按给定序列的bfs序搜

再比如树链剖分,调整dfs顺序,先搜重儿子后搜轻儿子

本题中,调整dfs顺序,先搜与之相连的边权最大的儿子

有一种一堆考生优先去逛全国各名校,学霸留在了985,学渣说再去别的学校看看,结果留在了三本的感觉

后来考上三本的学渣矢口否认,没说自己考不上,说自己没去过985

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
struct node
{
	int v,w;
	node(){
	}
	node(int vv,int ww):v(vv),w(ww){
	}
};
bool cmp(node a,node b)
{
	return a.w>b.w;
}
vector<node>E[maxn];
priority_queue<int>q;
int T,N,Q;
int u,v,w,x;
ll ans;
//先搜大的边 询问更大的才能在这个点留下 否则搜不到这个点 
void dfs(int u,int fa,int maxw)//假设1有虚父亲之间有-1的边 不然无法处理留在1节点的边权询问 
{
	for(int i=0;i<E[u].size();++i)
	{
		int v=E[u][i].v,w=E[u][i].w;
		if(v==fa)continue;
		dfs(v,u,max(w,maxw));
	}
	while(!q.empty()&&q.top()>maxw)//说明最大的会留在这里,或是叶子,或是砍掉上一个最大之后的叶子 
	{
		//printf("maxw:%d left:%d node:%d\n",maxw,q.size(),u);
		ans+=u;
		q.pop();
	} 
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&N,&Q);
		for(int i=1;i<=N;++i)
		E[i].clear();
		while(!q.empty())q.pop();
		for(int i=1;i<N;++i)
		{
			scanf("%d%d%d",&u,&v,&w);
			//边是双向的 但可以在dfs时转化为有根树 
			E[u].push_back(node(v,w));
			E[v].push_back(node(u,w));
		}
		for(int i=1;i<=N;++i)//先搜权值大的 
		sort(E[i].begin(),E[i].end(),cmp);
		for(int i=1;i<=Q;++i)
		{
			scanf("%d",&x);
			q.push(x);
		}
		ans=0;
		dfs(1,-1,-1);//dfs(当前节点,当前最大边权值) 
		printf("%lld\n",ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值