【BZOJ2876】【NOI2012】骑行川藏(数学,二分答案)

【BZOJ2876】【NOI2012】骑行川藏(数学,二分答案)

题面

BZOJ

题解

我们有一个很有趣的思路。
首先我们给每条边随意的赋一个初值。
当然了,这个初值不会比这条边的风速小。
那么,我们可以先计算一下当前所需要的总能量。
剩下的能量我们分成若干等份。
每次从所有的边中,选择一个加了这一份能量后,时间减少最多的那条边,让他提速。
直到我们所有的能量都分配完,此时答案一定最优。
所以,可以简化一下题意。
ks(vv)2=EU的情况下,最小化sv
然后剩下的部分我就去看看学长写的吧(因为我也不懂)
MashiroSky's Blog
主要是不知道为什么梯度向量就平行了
补充一下自己的几点理解:
首先能量和等于EU是一个函数,我们可以把它先在空间中表示出来。
然后最小化的值我们也可以看成一个函数,那么我们类似于地理中的等高线,
把所有等值的点的位置一圈一圈的全部向外拓展,当它第一次与能量构成的函数相交时,
并且这个交点一定是切点,此时取到的就是最小值了。
梯度向量由偏向量构成,其中偏向量的每一维分别对应这这个函数在每一维上的导数。
也就是把每一维分别看做主元,其他的都看作常量后求导。
也许梯度向量相等可以看做为在切点处,任何一维的增长量都相等?
假设我们默认梯度向量平行
那么,就有(v1,v2,v3....,vn)=λ(v1,v2,...,vn)
我们可以二分这个λ,然后求解出所有的速度。
求解速度的时候等价于解方程2λKv2(vv)=1
所有已知量都挪到右边,假设算完后的结果是c
那么就是v3vv2=c
我们找左边的零点,发现显然只有两个零点v0
并且我们最终的速度一定不会小于v,解方程的时候我们可以二分,
解的下界是max(0,v)
差不多就这些了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAX 11111
#define eps 1e-13
int n;
double Eu,S[MAX],K[MAX],V[MAX],v[MAX],ans;
bool check(double lam)
{
	double ret=0;
	for(int i=1;i<=n;++i)
	{
		double l=max(0.0,V[i]),r=1e9,c=-1/(2*lam*K[i]);v[i]=l;
		while(l+eps<=r)
		{
			double mid=(l+r)/2;
			if(mid*mid*(mid-V[i])<c)l=mid;
			else r=mid;
		}
		v[i]=l;ret+=K[i]*S[i]*(V[i]-v[i])*(V[i]-v[i]);
	}
	return ret<=Eu;
}
int main()
{
	scanf("%d%lf",&n,&Eu);
	for(int i=1;i<=n;++i)scanf("%lf%lf%lf",&S[i],&K[i],&V[i]);
	double l=-1e9,r=0,ret;
	while(l+eps<=r)
	{
		double mid=(l+r)/2;
		if(check(mid))l=mid,ret=mid;
		else r=mid;
	}
	check(ret);
	for(int i=1;i<=n;++i)ans+=S[i]/v[i];
	printf("%.10lf\n",ans);
	return 0;
}

posted @   小蒟蒻yyb  阅读(329)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示