您的位置:首页 > 运维架构

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),但或许并不是这样:

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);
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息