【牛客CSP-S提高组赛前集训营3】A - 货物收集【堆】

题目大意:

题目链接:https://ac.nowcoder.com/acm/contest/1102/A

Venn想要收集一些货物。

Venn有一颗n个节点的树,一开始Venn在1号节点,其他每个节点都有一定的货物储备,Venn只要经过那些节点,就可以收集到节点的所有货物。每个节点的货物只能收集一次。

显然,Venn并不能轻易的收集所有的货物。每一条连接着两个节点的路径,都有一个邪恶的怪物镇守。Venn的武力值必须不小于怪物的武力值才能安全地从这条路径上通过。

Venn一开始的武力值是0,但是她可以选择健身来提升自己的武力值。每健身一分钟,就会提升一点武力值。Venn并不想收集所有的货物,只要最终收集到的货物总量不低于W就可以了。Venn一旦开始收集,就不能再健身了。但是Venn的速度很快,可以认为收集货物和从路径上经过都不需要时间。

由于Venn还急着去颓废,所以她想让你帮她计算收集到指定数量的货物最少需要几分钟。


思路:

在任意一个时刻,我们把可以进行转移的所有边扔进一个小根堆里。然后取边权的最小值来转移。
很显然,如果我们不选择边权的最小值的话,因为最终答案是所有选择的边的边权最大值,那么无论选那一条边,边权最大值都不小于此时可选的边权最小值。换句话说,选择边权最小值来转移是最优的。
那么就记录点权和 s u m sum sum,如果某一个时刻转移使得 s u m ≥ m sum\geq m summ,那么就输出边权最大值即可。
时间复杂度 O ( m log ⁡ m ) O(m\log m) O(mlogm)
还有一种二分最小值转变成判定性问题的方法。时间复杂度是基本一样的。


代码:

#include <queue>
#include <cstdio>
#include <cstring>
#define mp make_pair
using namespace std;

const int N=1e6+10;
int n,m,tot,sum,maxn,a[N],head[N];
bool vis[N];
priority_queue<pair<int,int> > q;

struct edge
{
	int next,to,dis;
}e[N*2];

void add(int from,int to,int dis)
{
	e[++tot].to=to;
	e[tot].dis=dis;
	e[tot].next=head[from];
	head[from]=tot;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=2;i<=n;i++)
		scanf("%d",&a[i]);
	vis[1]=1;
	for (int i=1,x,y,z;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
		if (x==1) q.push(mp(-z,y)),vis[y]=1;
		if (y==1) q.push(mp(-z,x)),vis[x]=1;
	}
	while (1)
	{
		maxn=max(maxn,-q.top().first);
		int x=q.top().second;
		if (sum+a[x]>=m)
			return !printf("%d",maxn);
		sum+=a[x]; q.pop();
		for (int i=head[x];~i;i=e[i].next)
			if (!vis[e[i].to])
			{
				q.push(mp(-e[i].dis,e[i].to));
				vis[e[i].to]=1;
			}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值