XJTUOJ #1030 JM的星系战争

题目描述:

今天,带恶人JM掀起了一场超大规模的星系战争,为了阻止疯狂的JM,你需要前往n个星球去招募勇士(为了部落!)。这些星球的编号为1-n,地球(也就是你的初始所在)为1号星球。为了掩人耳目(?)你必须按照编号顺序依次访问这些星球,然后再回到地球上,也就是按照1( ightarrow)2( ightarrow)···( ightarrow)n( ightarrow)1的顺序访问。

当你驾驶飞船访问每个星球时,你的飞船都需要在星球上着陆然后再起飞,这两个过程都是需要耗费大量的燃料的。因为这是一艘神(zhi)奇(zhang)的飞船,它只在着陆和起飞时有大量消耗,至于在星球之间飞行所耗费的燃料,少到可以忽略不计。

对于编号为 (i) 的星球,着陆所需的燃料为(M_1)/(a_i),起飞所需的燃料为(M_2)/(b_i),其中(M_1)(M_2)分别表示着陆或起飞之前飞船的总重。飞船的总重可以表示为(M=m+f),其中(m)表示飞船的净重,(f) 表示飞船上当前搭载的燃料重量。

例如在编号为 (i) 的星球上着陆之前有(m=9,f=3,a_i=8,b_i=2),那么着陆需要((9+3)/8=1.5)吨燃料,而起飞需要((9+1.5)/2=5.25)吨燃料,因为着陆以后燃料被消费掉了,起飞之前(f=1.5)而不是(3)

注意,整个过程是最开始从地球起飞,然后依次在(2...n)号星球着陆然后起飞,最后在地球着陆。

因为JM的军队已经将这些星球上的燃料掠夺一空,所以你没法在路上补充燃料,你的燃料只能在出发前全部装好。

同样是因为JM的军队的疯狂掠夺,现在燃料已经十分紧缺,你希望花费最少的燃料来完成招募任务。请计算访问这些星球并返回地球所需的最少燃料。上述所有重量的计数单位均相同。

输入格式:

第一行两个正整数(n)(m),表示星球个数和飞船净重。

接下来(n)行,每行两个正整数(a_i,b_i),分别表示每个星球的着陆参数和起飞参数,其作用如题目所述。

输出格式:

输出一行一个浮点数表示答案。但是如果无论装载多少燃料都无法完成招募任务,则输出一个整数(-1)不带浮点。

当答案的绝对值误差不超过(10^{-4})时即会被评测通过。

保证如果有解,则解小于(10^{18})

思路:

首先,一个很明显的思路是可以二分答案,模拟验证是否可行,但是(n)很大,有超时风险。

注意到燃料要最少,所以在地球着陆后剩的燃料为(0),这就是我们的末状态。

那么可以想到倒推,既然知道末状态,那么末状态之前一步是什么呢?列方程:((m+Delta)/a_1=Delta)((Delta)是着陆消耗的燃料)。

移项得:(Delta=m/(a_1-1))

同理,若着陆或起飞后燃料为(ans),那么之前燃料的值为((m+a_i*ans)/(a_i-1))((m+b_i*ans)/(b_i-1))

代码如下:

#include<cstdlib>
#define db double
#define maxn (int)(2*1e5)+1
db a[maxn],b[maxn];
int main(){
	int i,j,n,x,y;
	db ans=0,m;
	scanf("%d%lf",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		if(x<=1||y<=1){
			printf("-1");
			return 0;
		}
		a[i]=x;b[i]=y;
	}
	ans=m/(a[1]-1);
	for(i=n;i>=2;i--){
		ans=(m+b[i]*ans)/(b[i]-1);
		ans=(m+a[i]*ans)/(a[i]-1);
	}
	ans=(m+b[1]*ans)/(b[1]-1);
	printf("%.6lf",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/landmine-sweeper/p/13739916.html