迭代
Memory: 420K Time: 438MS
https://code.csdn.net/snippets/1632673二分
Result: Time Limit Exceeded见错误分析和#现实
部分
TLE:https://code.csdn.net/snippets/1632674
Memory: 8292K Time: 1297MS
AC: https://code.csdn.net/snippets/1633558
写完了普通分数规划(最优比率数对组),最优比率环(POJ3621 Sightseeing Cows),下面该最优比率生成树啦!
找出图的一个生成树(求值即可),使树上 ∑树边上的iV[i]∑树边上的iC[i] 最大,V[i]为建立两点间路径所需架设电梯的高度(两点高度差值),也就是花费,C[i]为两点映射到地面的平面直角坐标系后的欧几里得距离。这两个数均是一条边的属性。
这里的分析和最优比率环神似!!其实就是照着改的。。
以前的01分数规划方式,是二分答案ans,计算可能达到的最大 ∑m个(V[i]−ans∗C[i]) 的值,通过它的正负判断最优解所在的区间
这道题里,没有明确规定找出的边数,但要求找出的几条边必须是给出图的生成树。如果还二分的话,什么可以是拿来判断的条件呢?
同样的思路,计算图中使得 ∑生成树上的边i(V[i]−ans∗C[i]) 最大的一个生成树,然后判断这个最大值的正负。
乍看之下计算“使得&%@#^*!最大的生成树”貌似不是多么容易实现。但是,如果拿出所有顶点,把以前的边权转化为 V[i]−ans∗C[i] ,然后求最大生成树,不就完美地实现了我们需要的要求吗?
上面的二分算法会超时。
上面的二分算法如果写得常数不好,很容易超时的。
Q:这还会超时?
A:其实写得好可以卡时过的,但是我写的代码的常数嘛,。。。
FYI:如果你对你的代码有信心,注意到这道题是完全图(稠密到了极点。。),试着把你代码里的kruskal换成无堆优化的Prim,可能能卡过。如果你是个神犇。。那请忽略这段话。。
我一开始二分没过,几近放弃,后来看到@Monster_Yi
神犇用二分过了,给了我无尽的动力——经过对我自己代码的仔细审查和比对,发现我又秀逗了······大家可以看到我第一次提交的TLE代码片里面二分的部分有这样的一段:
double p=prim0(mid), p1=prim(mid);
//printf("%.02f %.02f\n", p, p1);
if(p1>0) r=mid;
else l=mid;
woc…我求了两遍生成树。。!!prim0()
是带堆优化的prim,prim()
是裸的,因为看到Discuss里有童鞋说不带优化的prim更容易过,于是敲了一个,一开始调不对,于是就求了两个的值,输出比大小,后来调过了,却忘记删除调试语句。。去掉p=prim0(mid),
这句话,立马从TLE变成8292K 2594MS
,原来我的代码常数也不是不可救药吗~开心 o( ̄▽ ̄)ブ
还有还有,有一个更厉害的优化。这个优化能让时间直接从2500ms到1300ms——
减少常数的有效方式是减少重复计算:这里有什么计算结果是可以重用的呢?边权在每次二分时都会更改,显然不能重复使用,由此导致每次的最大生成树也没有可以重复使用的部分。但是,注意到边权的一部分是由两点投影的欧几里得距离组成的,对于任意两点间,这个数值在解决本CASE的生命周期内不会改变,可以重用。需要计算两个平方,还要开根号。这里会不会是个性能瓶颈呢?
double dist[maxn][maxn];
for(int i=1; i<=n; ++i) for(int j=i; j<=n; ++j)
dist[i][j]=dist[j][i]=pow(pow((double)abs(v[i].x-v[j].x), 2.0)+pow((double)abs(v[i].y-v[j].y), 2.0), 0.5);
inline double cost(int i1, int i2, double mid)
{
//double dist=pow(pow((double)abs(v1.x-v2.x), 2.0)+pow((double)abs(v1.y-v2.y), 2.0), 0.5);
double ret=mid*dist[i1][i2]-(double)abs(v[i1].h-v[i2].h);
return ret;
}
事实证明,是的。
注意到有冗余信息没有利用。
还记得最优比率环吗?我们可以计算有没有一个正环,却无法得知这个正环的组成,或者说很难得知它。而对于MST,我们不仅可以计算它的大小,还可以直接获得它的组成!
那么,我们知道了这棵最大生成树的边。这棵树,求出来一定是一个比ans优的解,或者低于高估了的ans,但是很接近它的解。那么我们可以直接根据它计算出这个解的大小,作为下次用来求生成树传入的ans。可以证明,只要最大解存在,且(很显然)满足二分性质,这个算法一定会收敛。
这道题的问题还是不是很多的啦~
主要就是判断精度是否达到时忘了取ABS再和EPS比较了。。又是秀逗导致的
double delt=b-ans;
if(delt<0) delt=-delt;
if(delt<1e-7) break;