最优化比率生成树

POJ 2728 Desert King(最优比率生成树) prim+二分

北京赛区的经典题目,最优比率生成树,传说中楼哥1A的G题。。。
什么是最优比率生成树呢?说白了很简单,已知一个完全图,每条边有两个参数(b和c),求一棵生成树,使(∑xi×ci)/(∑xi×bi)最小,其中xi当第i条边包含在生成树中时为1,否则为0。其实也可以看成一个0,1的整数规划问题。
我的做法是LRJ《算法艺术与信息学竞赛》中介绍的二分,详细的证明请看书,这里只是简单的介绍一下核心的方法:
1.首先找出这个比率的最小值和最大值 front,rear
2.求mid=(front+reat)/2
3.用 ci-mid*bi 重新构图
4.求出新图的最小生成树权值之和
5.如果权值等于0,mid就是我们要求的比率,结束。如果权值>0,front=mid,如果权值<0,rear=mid,跳回2继续循环。


不过这个算法对精度的要求比较高,我用0.001就错了,0.00001超时,只有0.0001AC,汗
另外时间效率也不高,3000MS的题,耗去了2500MS,看来这个算法还是有待改进。
下面是我的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{

    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];

inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=fabs(dot[i].h-dot[j].h)-l*dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}


inline  double prim( double graph[MAX][MAX], int n)
{
    bool visit[MAX]={0};
    int mark;
    double dis[MAX];
    double ans=0;
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
        dis[i]=graph[1][i];
    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        ans+=dis[mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
                dis[j]=graph[mark][j];
        }


    }

    return ans;
}



int main()
{

    int i,j;
    int n;
    double res;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        double front,rear;
        front=0;
        rear=100;//这个地方有点悬。。。
        double mid;
        double pre=0.0;
        while(front<=rear)
        {

            mid=(front+rear)/2;
            creat(n,mid);
            res=prim(graph,n);
            if(fabs(res-pre)<=0.0005)
                break;
            else if(res>0.0005)
                front=mid;
            else
                rear=mid;
        }

        printf("%.3lf\n",mid);
    }

    return 0;
}

———————————————————————传说中的分割线————————————————————————————
终于在今天下午 使用迭代法将此题优化到282MS,呵呵 这名字让我又想起了数值分析。。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{
    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];
double c[MAX][MAX];
double s[MAX][MAX];

inline  void creatcs( int n)
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            c[i][j]=fabs(dot[i].h-dot[j].h);
            s[i][j]=dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}



inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=c[i][j]-l*s[i][j];
        }

    }

}

double sumc;
double sums;

inline  void prim( double graph[MAX][MAX], int n)
{
    sumc=0;
    sums=0;
    bool visit[MAX]={0};
    int mark;
    int pre[MAX];
    double dis[MAX];
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
    {
        dis[i]=graph[1][i];
        pre[i]=1;
    }

    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        sumc+=c[pre[mark]][mark];
        sums+=s[pre[mark]][mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
            {
                dis[j]=graph[mark][j];
                pre[j]=mark;
            }

        }


    }

}



int main()
{

    int i,j;
    int n;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        creatcs(n);
        double prerate=30.0;
        double rate=30.0;
        while(true)
        {
            creat(n,rate);
            prim(graph,n);
            rate=sumc/sums;
            if(fabs(rate-prerate)<0.001)
                break;
            prerate=rate;
        }

        printf("%.3lf\n",rate);
    }

    return 0;
}

posted on 2009-09-04 23:53 abilitytao 阅读(2116) 评论(4)  编辑 收藏 引用


POJ 2728 Desert King(最优比率生成树) prim+二分

北京赛区的经典题目,最优比率生成树,传说中楼哥1A的G题。。。
什么是最优比率生成树呢?说白了很简单,已知一个完全图,每条边有两个参数(b和c),求一棵生成树,使(∑xi×ci)/(∑xi×bi)最小,其中xi当第i条边包含在生成树中时为1,否则为0。其实也可以看成一个0,1的整数规划问题。
我的做法是LRJ《算法艺术与信息学竞赛》中介绍的二分,详细的证明请看书,这里只是简单的介绍一下核心的方法:
1.首先找出这个比率的最小值和最大值 front,rear
2.求mid=(front+reat)/2
3.用 ci-mid*bi 重新构图
4.求出新图的最小生成树权值之和
5.如果权值等于0,mid就是我们要求的比率,结束。如果权值>0,front=mid,如果权值<0,rear=mid,跳回2继续循环。


不过这个算法对精度的要求比较高,我用0.001就错了,0.00001超时,只有0.0001AC,汗
另外时间效率也不高,3000MS的题,耗去了2500MS,看来这个算法还是有待改进。
下面是我的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{

    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];

inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=fabs(dot[i].h-dot[j].h)-l*dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}


inline  double prim( double graph[MAX][MAX], int n)
{
    bool visit[MAX]={0};
    int mark;
    double dis[MAX];
    double ans=0;
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
        dis[i]=graph[1][i];
    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        ans+=dis[mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
                dis[j]=graph[mark][j];
        }


    }

    return ans;
}



int main()
{

    int i,j;
    int n;
    double res;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        double front,rear;
        front=0;
        rear=100;//这个地方有点悬。。。
        double mid;
        double pre=0.0;
        while(front<=rear)
        {

            mid=(front+rear)/2;
            creat(n,mid);
            res=prim(graph,n);
            if(fabs(res-pre)<=0.0005)
                break;
            else if(res>0.0005)
                front=mid;
            else
                rear=mid;
        }

        printf("%.3lf\n",mid);
    }

    return 0;
}

———————————————————————传说中的分割线————————————————————————————
终于在今天下午 使用迭代法将此题优化到282MS,呵呵 这名字让我又想起了数值分析。。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{
    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];
double c[MAX][MAX];
double s[MAX][MAX];

inline  void creatcs( int n)
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            c[i][j]=fabs(dot[i].h-dot[j].h);
            s[i][j]=dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}



inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=c[i][j]-l*s[i][j];
        }

    }

}

double sumc;
double sums;

inline  void prim( double graph[MAX][MAX], int n)
{
    sumc=0;
    sums=0;
    bool visit[MAX]={0};
    int mark;
    int pre[MAX];
    double dis[MAX];
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
    {
        dis[i]=graph[1][i];
        pre[i]=1;
    }

    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        sumc+=c[pre[mark]][mark];
        sums+=s[pre[mark]][mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
            {
                dis[j]=graph[mark][j];
                pre[j]=mark;
            }

        }


    }

}



int main()
{

    int i,j;
    int n;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        creatcs(n);
        double prerate=30.0;
        double rate=30.0;
        while(true)
        {
            creat(n,rate);
            prim(graph,n);
            rate=sumc/sums;
            if(fabs(rate-prerate)<0.001)
                break;
            prerate=rate;
        }

        printf("%.3lf\n",rate);
    }

    return 0;
}

posted on 2009-09-04 23:53 abilitytao 阅读(2116) 评论(4)  编辑 收藏 引用

POJ 2728 Desert King(最优比率生成树) prim+二分

北京赛区的经典题目,最优比率生成树,传说中楼哥1A的G题。。。
什么是最优比率生成树呢?说白了很简单,已知一个完全图,每条边有两个参数(b和c),求一棵生成树,使(∑xi×ci)/(∑xi×bi)最小,其中xi当第i条边包含在生成树中时为1,否则为0。其实也可以看成一个0,1的整数规划问题。
我的做法是LRJ《算法艺术与信息学竞赛》中介绍的二分,详细的证明请看书,这里只是简单的介绍一下核心的方法:
1.首先找出这个比率的最小值和最大值 front,rear
2.求mid=(front+reat)/2
3.用 ci-mid*bi 重新构图
4.求出新图的最小生成树权值之和
5.如果权值等于0,mid就是我们要求的比率,结束。如果权值>0,front=mid,如果权值<0,rear=mid,跳回2继续循环。


不过这个算法对精度的要求比较高,我用0.001就错了,0.00001超时,只有0.0001AC,汗
另外时间效率也不高,3000MS的题,耗去了2500MS,看来这个算法还是有待改进。
下面是我的代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{

    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];

inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=fabs(dot[i].h-dot[j].h)-l*dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}


inline  double prim( double graph[MAX][MAX], int n)
{
    bool visit[MAX]={0};
    int mark;
    double dis[MAX];
    double ans=0;
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
        dis[i]=graph[1][i];
    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        ans+=dis[mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
                dis[j]=graph[mark][j];
        }


    }

    return ans;
}



int main()
{

    int i,j;
    int n;
    double res;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        double front,rear;
        front=0;
        rear=100;//这个地方有点悬。。。
        double mid;
        double pre=0.0;
        while(front<=rear)
        {

            mid=(front+rear)/2;
            creat(n,mid);
            res=prim(graph,n);
            if(fabs(res-pre)<=0.0005)
                break;
            else if(res>0.0005)
                front=mid;
            else
                rear=mid;
        }

        printf("%.3lf\n",mid);
    }

    return 0;
}

———————————————————————传说中的分割线————————————————————————————
终于在今天下午 使用迭代法将此题优化到282MS,呵呵 这名字让我又想起了数值分析。。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using  namespace std;
#define MAX 1001
#define INF 1000000000
struct node
{
    double x,y,h;
}
dot[MAX];


inline  double dis( double x1, double y1, double x2, double y2)
{

    return sqrt( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) );
}



double graph[MAX][MAX];
double c[MAX][MAX];
double s[MAX][MAX];

inline  void creatcs( int n)
{
    int i,j;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n;j++)
        {
            c[i][j]=fabs(dot[i].h-dot[j].h);
            s[i][j]=dis(dot[i].x,dot[i].y,dot[j].x,dot[j].y);
        }

    }

}



inline  void creat( int n, double l)
{
    int i,j;
    for(i=1;i<=n;i++)
    {

        for(j=1;j<=n;j++)
        {

            graph[i][j]=c[i][j]-l*s[i][j];
        }

    }

}

double sumc;
double sums;

inline  void prim( double graph[MAX][MAX], int n)
{
    sumc=0;
    sums=0;
    bool visit[MAX]={0};
    int mark;
    int pre[MAX];
    double dis[MAX];
    int i,j;
    visit[1]=true;
    for(i=1;i<=n;i++)
    {
        dis[i]=graph[1][i];
        pre[i]=1;
    }

    for(i=1;i<n;i++)
    {

        int minnum=INF;
        for(j=1;j<=n;j++)
        {

            if(!visit[j]&&dis[j]<=minnum)
            {
                minnum=dis[j];
                mark=j;
            }

        }

        visit[mark]=true;
        sumc+=c[pre[mark]][mark];
        sums+=s[pre[mark]][mark];
        for(j=1;j<=n;j++)
        {
            if(!visit[j]&&graph[mark][j]<dis[j])
            {
                dis[j]=graph[mark][j];
                pre[j]=mark;
            }

        }


    }

}



int main()
{

    int i,j;
    int n;
    while(scanf("%d",&n))
    {

        if(n==0)
            break;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&dot[i].x,&dot[i].y,&dot[i].h);
        }

        creatcs(n);
        double prerate=30.0;
        double rate=30.0;
        while(true)
        {
            creat(n,rate);
            prim(graph,n);
            rate=sumc/sums;
            if(fabs(rate-prerate)<0.001)
                break;
            prerate=rate;
        }

        printf("%.3lf\n",rate);
    }

    return 0;
}

posted on 2009-09-04 23:53 abilitytao 阅读(2116) 评论(4)  编辑 收藏 引用


你可能感兴趣的:(最优化比率生成树)