Topcoder SRM 543 DIV2 1000 EllysThreeRivers & DIV1 500 EllysRivers
2016-09-13 17:33
561 查看
Task
有四座并排放置,宽度忽略不计,长度均为length(1<=length<=1000)的小岛,分别记为0~3;小岛之间都隔着一条河,分别记为0~2。小C在陆地上行走的速度为walk(1<=walk<=100),对于河i,知道它的宽度width[i](1<=width[i]<=1000)以及小C在该河流中的游速swim[i](1<=swim[i]<=100)(忽略河流游速)。
求小C从0号小岛最南端到3号小岛最北端的距离。
Solution
本题难在如何运用所推公式。先总结一下3条结论:
1)在岸上走路的时间几乎没有。我们唯一需要行走的时间或许只是当walk>swim[i]的时候,因为如果walk<swim[i],那么可以在河中平行着河岸游,但显然平行河岸游反而会浪费时间。然而根据样例,也仍然出现了根本不需要在陆地行走的情况。
2)所有的陆地行走路程可以归到最后一个小岛上。
3)小C不一定只向东游,最优解应该是向东北游。现在我们只考虑两座岛的情况,那么假设小C向北方向的位移为X(0<=X<=length),则所要花费的时间为:
Δt=X2+width[i]2−−−−−−−−−−−−√swim[i]+length−Xwalk大概可以从这个直观的式子中看出些什么,譬如说一些枚举或者搜索?反正如果一条河可以这样求出来,那三条河也同样可以求。写一个三元的搜索,或者是一个递归函数就可以了。
具体分析上面那个式子,左侧在不断增加的时候,右侧又在不断递减,可以预料到一般情况下,这个式子跟二次函数一样具有一个最低点,即最小值,而此时的X也处于临界处。采用二分枚举么,但它的01性并不是线性。所以我们采用三分枚举或者以斜率作为01性的二分枚举。
用三分枚举的时间复杂度应该是O(loglength3),但或许并不是这样:
上面代码中的二分跟通常的不一样,枚举了二分的次数。
原因很显然:因为我们二分的值是浮点数,是可能会出现一直在不断二分而L和R始终不会相同的情况,导致陷入死循环。解决方案有两个:
1)在估算精度适宜的情况下,强制让其二分p次;
2)如题解所说,设置一个极小的常数EPS=10−9,规定误差<EPS就可以退出循环。
然后简单的数论做法用到了偏导数,看不懂跳过。
DIV1 500:
Task
其他描述不变,但是小C选择乘船,只有在每个整数点才有船港,并且只能从这边的船港到另一边的船港。同时数据范围分别扩增为:length<=10^5,N(小岛个数)<=50,walk,width[i],swim[i]<=10^6。
Solution
虽然Div2加强了数据范围,但还是将原来难转移的double pos变成了int pos,可以说算是有利有弊。做法不一样但是思路仍然可以借鉴。
如果我们采用Dijkstra算法(会whatever_limit_exceed)或者dp,结合三分查找可以达到O(N∗length∗log2length)的复杂度。(这里或许还要再分析一下为什么是O(N∗length∗log2length):首先如果采用动态规划求解,最暴力的做法是对于每条河,枚举西侧的船港与东侧的船港。此时需要O(N∗length2)的复杂度,而最后O(length)的目的是为了找最小值,那么上述三分枚举就可以缩小到近似O(log2length)。)
既然已经优化到O(log2length)了,我们可以尝试优化到O(1):对于西岸的两个相邻的船港pos与pos−1,分别行驶到东岸的to(pos)与to(pos−1)。有一个直观的结论:point(pos)>=point(pos−1)因为它满足单调性,具体的证明可以通过YY。有了这个结论,我们可以从length到0反序枚举船舱,用point指向另一边的船舱然后滑动,这样p平摊下来还是O(length)的复杂度。于是此题得解。
有四座并排放置,宽度忽略不计,长度均为length(1<=length<=1000)的小岛,分别记为0~3;小岛之间都隔着一条河,分别记为0~2。小C在陆地上行走的速度为walk(1<=walk<=100),对于河i,知道它的宽度width[i](1<=width[i]<=1000)以及小C在该河流中的游速swim[i](1<=swim[i]<=100)(忽略河流游速)。
求小C从0号小岛最南端到3号小岛最北端的距离。
Solution
本题难在如何运用所推公式。先总结一下3条结论:
1)在岸上走路的时间几乎没有。我们唯一需要行走的时间或许只是当walk>swim[i]的时候,因为如果walk<swim[i],那么可以在河中平行着河岸游,但显然平行河岸游反而会浪费时间。然而根据样例,也仍然出现了根本不需要在陆地行走的情况。
2)所有的陆地行走路程可以归到最后一个小岛上。
3)小C不一定只向东游,最优解应该是向东北游。现在我们只考虑两座岛的情况,那么假设小C向北方向的位移为X(0<=X<=length),则所要花费的时间为:
Δt=X2+width[i]2−−−−−−−−−−−−√swim[i]+length−Xwalk大概可以从这个直观的式子中看出些什么,譬如说一些枚举或者搜索?反正如果一条河可以这样求出来,那三条河也同样可以求。写一个三元的搜索,或者是一个递归函数就可以了。
具体分析上面那个式子,左侧在不断增加的时候,右侧又在不断递减,可以预料到一般情况下,这个式子跟二次函数一样具有一个最低点,即最小值,而此时的X也处于临界处。采用二分枚举么,但它的01性并不是线性。所以我们采用三分枚举或者以斜率作为01性的二分枚举。
用三分枚举的时间复杂度应该是O(loglength3),但或许并不是这样:
class EllysThreeRivers { public: int Length,Walk,Wid[3],Swim[3]; double rec(int c,double dis){ if(c==3)return (1.0*Length-dis)/(1.0*Walk); double L=dis,R=1.0*Length,res=0,ans=0; for(int p=0;p<100;p++){ double Lmid=L+(R-L)/3.0,Rmid=R-(R-L)/3.0; double ansL=sqrt((Lmid-dis)*(Lmid-dis)+Wid[c]*Wid[c])/(1.0*Swim[c])+rec(c+1,Lmid); double ansR=sqrt((Rmid-dis)*(Rmid-dis)+Wid[c]*Wid[c])/(1.0*Swim[c])+rec(c+1,Rmid); if(ansL>ansR)res=Rmid,ans=ansR,L=Lmid; else res=Lmid,ans=ansL,R=Rmid; } return ans; } double getMin(int length,int walk,vector<int> width,vector<int> swim) { Length=length,Walk=walk; for(int i=0;i<3;i++) Wid[i]=width[i],Swim[i]=swim[i]; return rec(0,0.0); } };
上面代码中的二分跟通常的不一样,枚举了二分的次数。
原因很显然:因为我们二分的值是浮点数,是可能会出现一直在不断二分而L和R始终不会相同的情况,导致陷入死循环。解决方案有两个:
1)在估算精度适宜的情况下,强制让其二分p次;
2)如题解所说,设置一个极小的常数EPS=10−9,规定误差<EPS就可以退出循环。
然后简单的数论做法用到了偏导数,看不懂跳过。
DIV1 500:
Task
其他描述不变,但是小C选择乘船,只有在每个整数点才有船港,并且只能从这边的船港到另一边的船港。同时数据范围分别扩增为:length<=10^5,N(小岛个数)<=50,walk,width[i],swim[i]<=10^6。
Solution
虽然Div2加强了数据范围,但还是将原来难转移的double pos变成了int pos,可以说算是有利有弊。做法不一样但是思路仍然可以借鉴。
如果我们采用Dijkstra算法(会whatever_limit_exceed)或者dp,结合三分查找可以达到O(N∗length∗log2length)的复杂度。(这里或许还要再分析一下为什么是O(N∗length∗log2length):首先如果采用动态规划求解,最暴力的做法是对于每条河,枚举西侧的船港与东侧的船港。此时需要O(N∗length2)的复杂度,而最后O(length)的目的是为了找最小值,那么上述三分枚举就可以缩小到近似O(log2length)。)
既然已经优化到O(log2length)了,我们可以尝试优化到O(1):对于西岸的两个相邻的船港pos与pos−1,分别行驶到东岸的to(pos)与to(pos−1)。有一个直观的结论:point(pos)>=point(pos−1)因为它满足单调性,具体的证明可以通过YY。有了这个结论,我们可以从length到0反序枚举船舱,用point指向另一边的船舱然后滑动,这样p平摊下来还是O(length)的复杂度。于是此题得解。
#define wid first #define swi second class EllysRivers { public: static const int M=100005; pair<double,double>Data[55]; double dp[2][M]; double sqr(double x){return x*x;} double eval(int N,int length,int walk){ int cur=0; for(int i=0;i<=length;i++) dp[cur][length-i]=i*1.0/walk; for(int i=N-1;i>=0;--i){ int pos=length;cur^=1; for(int j=length;j>=0;--j){ dp[cur][j]=dp[cur^1][pos]+sqrt(sqr(Data[i].wid)+sqr(1.0*pos-j))/Data[i].swi; while(pos>j){//合计O(n)找最小值 double nxt=dp[cur^1][pos-1]+sqrt(sqr(Data[i].wid)+sqr(1.0*pos-1-j))/Data[i].swi; if(nxt>dp[cur][j])break; dp[cur][j]=nxt;pos--; } } } return dp[cur][0]; } double getMin(int length,int walk,vector<int> width,vector<int> speed) { int N=width.size(); for(int i=0;i<N;i++){ Data[i].wid=width[i]; Data[i].swi=speed[i]; } return eval(N,length,walk); } };
相关文章推荐
- EllysThreeRivers(SRM543-div2-3)
- 【SRM543 Div2 1000】 EllysThreeRivers
- Topcoder: Problem Statement SRM 39 DIV1 500 Point
- Topcoder SRM 582 DIV2 500
- Topcoder SRM 635 Div2 1000 (一种 O(n) 求一棵树中最长连续边 长度的方法)
- Topcoder SRM 636 div2 1000
- topcoder SRM 543 div2 250
- Topcoder SRM 619 DIv2 500 --又是耻辱的一题
- Topcoder SRM 635 div2 1000
- TopCoder SRM 633 Div2 Problem 500 - Jumping
- Topcoder SRM 636 Div2 1000(切蛋糕,最后一块给自己。最小值中求最大值,二分+枚举)
- TOPCODER/SRM 566 DIVII(250、500、1000题)(1000PT暂未附上代码)
- TopCoder SRM 474 DIV1 1000
- TopCoder SRM 360 DIV2 500 分题目, 需要排列组合么?需要强力法么?
- Topcoder SRM 148 Div2 1000(dfs搜索+hash判重)
- TopCoder SRM 543 DIV2
- srm575_div1&2_1000(网络流)
- SRM 606 div2 500 EllysNumberGuessing
- topcoder SRM 548 DIV2 500
- Topcoder SRM 152 Div2 1000(状态压缩呀)