01分数规划最优比率生成树二分

#include 
#define eps 1e-6
using namespace std;
int n;
double x[1010],y[1010],h[1010],dis[1010];
bool vis[1010];
double d(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}
bool check(double l){
    double z=0.0;
    memset(vis,0,sizeof(vis));
    vis[1]=1,dis[1]=0;								//从一号点开始搜
    for(int i=2;i<=n;i++)dis[i]=d(1,i)-l*fabs(h[1]-h[i]);		//一号点到每个点的DIS值
    for(int i=1;imx) mx=dis[j],mni=j;	//得本轮循环DIS最大的点
        vis[mni]=1;		//标为已访问,即连边,但是其实是点集中谁连过去的并不重要
        z+=dis[mni];		//最大生成树累加,每次都是求F表达式的最大值
        for(int j=1;j<=n;j++)//从最小点开始往外射出更新最短距离
            if (!vis[j]) dis[j]=max(dis[j],d(mni,j)-l*fabs(h[mni]-h[j]));
    }
if (z<0) return 0;//如果L太大的话,DIS数组的值会是负值来的
    else return 1;
}
int main(){
    while(scanf("%d",&n)&&n){
        bool flag=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf",&x[i],&y[i],&h[i]);
            for(int j=1;j=eps){
            double mid=(l+r)/2;
            if (check(mid)) l=mid;
            else r=mid;
        }
        printf("%.3f\n",1.0/r);
    }
    return 0;
}

个人认为关键:就是最大生成树的时候边权改为F[cnt]表达式

原题是求ABS/DIS=CNT最小值,代码改为求DIS/ABS=CNT最大值

然后令DIS-CNT*ABS0,当F小于0时,CNT更大不存在,当F大于0时,CNT可变大更优

 

POJ-2728-01

题目大意:平面上有nn个点,每个点上有一个高度h(i)h(i),在两个点之间修建管道所需的费用是|h(i)−h(j)||h(i)−h(j)|,管道的长度是两个点的欧几里得距离,要求一棵生成树使得费用和与管道总长比值最小,求这个最小比值。

Sample Input

4

0 0 0

0 1 1

1 1 2

1 0 3

0

Sample Output

1.000

构造一个函数:

其中F(r)在平面坐标系上体现为一条直线,每一组x[i]都分别唯一地对应一条直线,这些直线的截距均大于等于0、斜率均小于等于0。而这些直线在X轴上的截距就是这一组x求出来的r,而截距的最大值就是我们要求的R。(如下图所示)

个人理解:每次都控制DISABS使得表达式F(R)得到最大值,至于R是落在临界左边还是右边是不归DISABS管的,反正DISABS就是要变成那条最大的直线,像本题不断更新DIS,就是表达式的值变最大,加到最后还是小于零,说明R就是大于临界的,记好:就是往界点走就更优

01分数规划最优比率生成树二分_第1张图片

在X轴上面任取一个r,如果至少有一条直线的F(r)>0,那么说明了什么呢?

说明至少还有一条直线与X轴的交点在它的右边,那么这个r一定不是最大值,真正的最大值在它的右边。反之,如果所有的F(r)都小于0,那么真正的最大值在它的左边

那么前面的结论就可以换种说法,因为我只需判断最大的那个F(r)的正负性就行了:

随便取一个r,如果F(r)max>0,则结果R>r,反之若F(r)max<0,则结果R

你可能感兴趣的:(ACM笔记-3图流)