POJ2728 Desert King

来源:互联网 发布:nginx和zookeeper 编辑:程序博客网 时间:2024/06/12 01:11

分数规划->最优比率生成树+(二分/迭代)

迭代
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]ansC[i]) 的值,通过它的正负判断最优解所在的区间

这道题里,没有明确规定找出的边数,但要求找出的几条边必须是给出图的生成树。如果还二分的话,什么可以是拿来判断的条件呢?
同样的思路,计算图中使得i(V[i]ansC[i])最大的一个生成树,然后判断这个最大值的正负。
乍看之下计算“使得&%@#^*!最大的生成树”貌似不是多么容易实现。但是,如果拿出所有顶点,把以前的边权转化为V[i]ansC[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;
1 0
原创粉丝点击