[NOI2012] 騎行川藏
一、題目
二、解法
據說導數的相關內容是選擇性必修 \(2\) 的,考慮到我也沒有學過,那我就簡單的講一講吧,我只講怎麼求函式 \(y=ax^n\) 的導數:
\[d=\frac{a(x+\Delta x)^n-ax^n}{\Delta x}=?\Delta x^{?}x^{?}+anx^{n-1} \]因為最後包含 \(\Delta x\) 的項可以視而不見,我們用二項式展開可以得到唯一剩下的項是 \(anx^{n-1}\) ,而他就是導數。如果是若干個上述形式的函式加起來,那麼不難得到他們的導數也相加。
提示:必然存在一種最優的體力方案滿足:蛋蛋在每段路上都採用勻速騎行的方式。
首先考慮一些消耗能量最少的走法:如果 \(v[i]\) 大於 \(0\) ,那麼我們就使 \(v=v[i]\) ;如果 \(v[i]\leq0\) ,那麼我們使得 \(v=0\) 。這樣顯然不是最優的,但是我們可以慢慢的調整使其達到最優,這種方法叫做 微調法 。
但是這道題是在實數範圍內的最優問題,我們無法用最小單位來微調。那麼我們不妨用極限的思想,我們把無限小的能量用於給某段路減少時間,那麼這道題就必須要引入 導數 的概念。即每段路都擁有一個導數,我們每次取導數最大的微調。
我們可以想象一個 \(E-t\) 的影象 ,這個影象的導數是越來越大的(導數永遠是負數,後面投入的相同能量會產生更小的效益),因為微調的過程是不可能實現的,我們可以利用這個性質來達到微調的效果。可以二分一個導數值 \(x\)
如果算出來消耗的能量大於已經有的能量,那麼就把導數值調小,否者就把導數值調大。怎麼算需要消耗的能量呢?首先我們要知道每段路的導數表示式,那麼我們就可以反解出速度。現在我們來推導導數 \(d\) 的表示式:
\[d=\frac{\Delta t}{\Delta E} \]我們知道 \(t=\frac{s}{v},E=sk(v-v')^2\),發現 \(t\) 和 \(E\) 好像是沒有關聯的,所以上面的 \(\Delta t\) 就不是很好求,那麼我們就用 \(v\) 把他們關聯起來,也就是求出 \(v\)
那麼可以用 \(d\) 反解出 \(v\) ,這是個一元三次方程,但是具有單調性,所以可以二分解決。
本題是二分套二分,方便卡時間我們不規定二分的精度而規定二分的次數,外層二分我做了 \(100\) 次,內層二分我做了 \(60\) 次,只要卡著時間你儘量多分幾次唄。
最後補充一點,你知不知道提示的勻速騎行的策略是怎麼來的。我們假設有一條路上不勻速騎行,那麼我們可以把它劃分成無限個勻速騎行的段,這些段的 \(k\) 和 \(v'\) 都相同,最後達到的 \(d\) 也相同,那麼由於導數的單調性解出來的 \(v\) 也都是相同的,這與一開始的假設相悖。所以我們用反證法說明了這個結論。
#include <cstdio>
#include <iostream>
using namespace std;
#define db double
const int M = 10005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n;db E,s[M],k[M],v[M];
db getv(db x,int i)
{
db l=max(0.0,v[i]),r=100000,mid;int cnt=60;
while(cnt--)
{
mid=(l+r)/2;
if(2*k[i]*x*mid*mid*(mid-v[i])>-1) l=mid;
else r=mid;
}
return (l+r)/2;
}
signed main()
{
n=read();scanf("%lf",&E);
for(int i=1;i<=n;i++)
{
scanf("%lf %lf %lf",&s[i],&k[i],&v[i]);
}
db l=-1e10,r=0,mid,sum;int cnt=100;
while(cnt--)
{
mid=(l+r)/2,sum=0;
for(int i=1;i<=n;i++)
{
db x=getv(mid,i);
sum+=s[i]*k[i]*(x-v[i])*(x-v[i]);
}
if(sum<=E) l=mid;
else r=mid;
}
db ans=0;mid=(l+r)/2;
for(int i=1;i<=n;i++)
{
ans+=s[i]/getv(mid,i);
}
printf("%.6f\n",ans);
}