【JZOJ B组】过河

3 篇文章 0 订阅

Description
有(N+1)个平行于y轴的河岸排成一排,每两个河岸之间夹着一条河,所以一共有N条河。第i 条河的宽度为wi,ryz在第i 条河中行进的速度为vi。河岸的宽度忽略不计。令X=sigma(wi)。

规定:

1、ryz从(0,0)出发,终点是(X,Y)。Y是一个给定的整数。

2、ryz在渡河时,必须从一个整点驶向另一个整点,花费的时间为这两个点的欧几里得距离除以速度。

3、ryz可以在河岸上行走,但也是必须从一个整点走向另一个整点,速度为给定的u。

求花费时间的最小值。

Input
第一行三个正整数N,Y,u。

第二行N个正整数表示wi。

第三行N个正整数表示vi。

Output
输出一行答案,保留4位小数。

Sample Input
2 3 1

5 5

1 1

Sample Output
10.4842

Data Constraint
对于30%的数据,N<=50,Y<=500;

对于60%的数据,N<=50;

对于100%的数据,N<=50000;

对于100%的数据,u<=105,wi<=105,vi<=105,Y<=105。

Hint
样例解释

Ryz先从(0,0)渡过第一条河到达(5,1),花费时间为5.0990,然后渡过第二条河到达(10,3),花费时间为5.3852。

先到(5,2)再到(10,3)也一种最优解。

思路

如果现在让你找一条路线(不一定最优),你一定会选择先向下走过N行后向右走到第Y列,如图。总时间可以马上计算出来。

这时候如果将路线分段,每一段为路线中其中一行到下一行的路线,可以考虑让其中一段路稍稍改变,让总时间减少。为什么要这样考虑?因为改变这一段路线其实并不改变其他N-1段路线的长度以及时间(显然)。怎么改变?当然是将下一行上的端点向右移动1个单位(为什么不能向左移动?显然这只会增加时间):

这样改变对总时间的影响是多少呢?因为上图中这条绿色的倾斜的路线实际上替代了2段粉色的路线,因此对总时间的增加或减少是可以计算出来的(怎么计算??可以用走绿色斜线的时间减去走原路线即2段粉色路线的时间)。如果总时间减少了那么这次路线改变就是成功的。对于N段路线我们都可以考虑这样的改变,最多可以改变Y次(为什么?请仔细思考)。
  那么一个基于贪心思想的解法就出来了:每一次在N段路线中选择1条改变路线后对总时间影响最大的路线进行改变,直至改变了Y次或者没有能减少时间的改变为止。由于实际上要在N个数中找一个最小的数并且进行修改,因此可以用一个堆维护这N个数,在O(logN)的时间内就可以完成一次修改,总的时间复杂度为O(YlogN)。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=1e5+77;
struct A
{
	int w,v,y;
	double t;
}a[maxn];
int n,y,u,w[maxn],v[maxn],cnt;
double ans;
double dis(int x,int y)
{
	return sqrt(x*x+y*y);
}
void up(int x)
{
	while(x/2>0)
	{
		if(a[x/2].t>a[x].t) swap(a[x/2].t,a[x].t); else break;
		x>>=1;
	}
}
void down(int x)
{
	int t=x;
	while(t*2<=cnt)
	{
		int p=t*2;
		if(p+1<=cnt&&a[p].t>a[p+1].t) p++;
		if(a[t].t>a[p].t) swap(a[t],a[p]);else break;
		t=p;
	}
}
void ins(A x)
{
	a[++cnt]=x; up(cnt);
}
void pop()
{
	swap(a[1],a[cnt]); cnt--; down(1);
}
A top()
{
	return a[1];
}
int main()
{
	scanf("%d%d%d",&n,&y,&u);
	for(int i=1; i<=n; i++) scanf("%d",&w[i]);
	for(int i=1; i<=n; i++) scanf("%d",&v[i]);
	for(int i=1; i<=n+2; i++) a[i].t=2147483647;
	for(int i=1; i<=n; i++)
	{
		A p;
		p.t=(dis(1,w[i])-w[i])/(double)v[i];
		ans+=(double)w[i]/v[i];
		p.w=w[i]; p.v=v[i]; p.y=0;
		ins(p);
//		printf("w=%d v=%d w[i]/v[i]=%.7lf s=%.7lf\n",w[i],v[i],(double)w[i]/v[i],ans);
	}
	A p;
	p.t=(double)1/u; p.w=0; p.v=u; p.y=0; ins(p);
	for(int i=1; i<=y; i++)
	{
		A p=top(); pop();
		ans+=p.t;
//		printf("t=%.7lf s=%.7lf\n",p.t,ans);
		p.y++; 
		p.t=(double)(dis(p.w,p.y+1)-dis(p.w,p.y))/p.v;
		ins(p);
	}
	printf("%.4lf",ans);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为OD考试中的问题是:士兵过河的情境。士兵过河是一道经典的逻辑思维题目,要求在特定条件下找到一种最佳解决方案。 在这个情景中,士兵需要过一条河,河上只有一条船可供使用。然而,在河的两岸和船上都有一些限制条件。首先,士兵们只能以一次过河的方式进行,也就是每次只能有一个人或一人乘船。其次,船的容量有限,只能乘坐有限数量的人。最后,船上必须有人才能使其正常行驶。 为了解决这道题目,需考虑以下几个策略。首先,可以利用船的来回行驶,逐个运送士兵。例如,士兵A、B、C、D需要过河,如果船只能容纳两人,则可以让A和B先过去,然后A再返回,接C和D过去,最后再返回接A。其次,还可以根据士兵的能力和速度进行合,使过河时间最短。例如,如果A花费的时间最长,可以让A和B一起过河,然后A返回,接C和D过河,这样可以减少整体的过河时间。 在解决这道题目的过程中,需要全面考虑船的容量和限制条件,合理安排士兵的过河顺序和合方式,以最优的方式完成过河任务。这道题目旨在考察考生的逻辑思维能力、织协调能力和问题解决能力,同时也考察了考生在有限资源条件下做出最优选择的能力。通过这样的考题,华为OD考试能够评估出考生的思维方式和解决问题的能力,从而筛选出最适合职位需求的人才。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值