首页 > 代码库 > poj 2728(Desert King) 最优比例生成树 (分数规划)

poj 2728(Desert King) 最优比例生成树 (分数规划)


      这题是最小生成树的一种扩展,就是求一棵树  使得 k =sigma(cost)/ sigma(len)  最小。   其中cost 为每条边花费,len为长度。  这实际上就是一个最优比例生成树。最优比例生成树的求解使用了分数规划的方法。  我们先任取k,假设k是最小值,那么sigma(ccost)-k*sigma(len)==0  。那么我们就新建图边权 为  ccosti-k*leni  。求一次最小生成树,如果生成树权值小于0,那么书名其实k还是有减小的空间的,否则,k不可能是最小。这样我们就可以用2分写了。  不过还有一种求解手段,就是用牛顿下山进行迭代搜索。这种搜索更快。  还有一点 这题只能用prim 求最小生成树 ,kural 会tle的。


方法1.   2分

VIEW CODE

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mmax=500010;
const int mod=20071027;
const double eps=1e-8;
typedef long long LL;
struct Point
{
    double x,y,z;
    void read()
    {
        scanf("%lf %lf %lf",&x,&y,&z);
    }
}P[1100];
int sgn(double x)
{
    if(fabs(x)<eps)
        return 0;
    return x<0?-1:1;
}
int n;
double cost[1100][1100];
double len[1100][1100];
double dis(Point x,Point y)
{
    return sqrt( (x.x-y.x)*(x.x-y.x) + (x.y-y.y)*(x.y-y.y) );
}
double Dis[1100];
bool vis[1100];
double prim(double t)
{
    for(int i=1;i<=n;i++)
        Dis[i]=1e20,vis[i]=0;
    Dis[1]=0;
    double ans=0.0;
    for(int i=1;i<=n;i++)
    {
        double mink=1e20;
        int e=-1;
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&Dis[j]<mink)
            {
                mink=Dis[j];
                e=j;
            }
        }
        vis[e]=1;
        ans+=Dis[e];
        for(int j=1;j<=n;j++)
        {
            if(Dis[j]>cost[e][j]-t*len[e][j])
                Dis[j]=cost[e][j]-t*len[e][j];
        }
    }
    return ans;
}
void solve()
{
    double l=0,r=100000000,mid;
    while(sgn(r-l)>0)
    {
        mid=(l+r)/2;
        double mm=prim(mid);
        if(sgn(mm) == 0)
            break;
        if(sgn(mm) < 0)
            r=mid;
        else
            l=mid;
    }
    printf("%.3lf\n",mid);
}
int main()
{
    while(cin>>n&&n)
    {
        for(int i=1;i<=n;i++)
            P[i].read();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                len[i][j]=dis(P[i],P[j]);
                cost[i][j]=abs(P[i].z-P[j].z);
            }
        }
        solve();
    }
    return 0;
}

方法2:  牛顿下山法

VIEW  CODE

#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<iostream>
#include<map>
#include<vector>
#include<algorithm>
#include<stdlib.h>
#include<set>
#include<ctime>
#include<cmath>
#define ex 2.7182818284590452354
#define pi acos(-1.0)
#define DC(n) printf("Case #%d:",++n)
#define SD(n) scanf("%d",&n)
#define SS(str) scanf("%s",str)
#define SDB(n) scanf("%lf",&n)
#define mm 1000000007
#define mmax 1100
#define eps 1e-5
#define inf 0x7fffffff
using namespace std;
typedef __int64 LL;

struct point
{
    int x,y,z;
}P[mmax];
int n;
double cost[mmax][mmax];
double dis[mmax][mmax];
double get_dis(point x,point y)
{
    return sqrt(1.0*(x.x-y.x)*(x.x-y.x)+1.0*(x.y-y.y)*(x.y-y.y));
}

double Dis[mmax];
bool vis[mmax];
int pre[mmax];
double prim(double mid)
{
    for(int i=1;i<=n;i++)
    {
        Dis[i]=cost[1][i]-mid*dis[1][i];
        pre[i]=1;
    }
    double Cost=0,Len=0;
    memset(vis,0,sizeof vis);
    vis[1]=1;
    Dis[1]=0;
    for(int i=1;i<n;i++)
    {
        double mink=inf;
        int e;
        for(int j=2;j<=n;j++)
        {
            if(!vis[j]&&Dis[j]<=mink)
            {
                mink=Dis[j];
                e=j;
            }
        }
        vis[e]=1;
        Cost+=1.0*abs(P[e].z-P[pre[e]].z);
        Len+=dis[e][pre[e]];
        for(int j=1;j<=n;j++)
        {
            if(!vis[j]&&Dis[j]>cost[e][j]-mid*dis[e][j])
            {
                Dis[j]=cost[e][j]-mid*dis[e][j];
                pre[j]=e;
            }
        }
    }
    return Cost/Len;
}
void Dink()
{
    double l=0,ans;
    while(1)
    {
        ans=prim(l);
        if(fabs(ans-l)<=eps)
            break;
        l=ans;
    }
    printf("%.3lf\n",ans);
}
int main()
{
    while(cin>>n&&n)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d %d",&P[i].x,&P[i].y,&P[i].z);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cost[i][j]=1.0*abs(P[i].z-P[j].z);
                dis[i][j]=get_dis(P[i],P[j]);
            }
        }
        Dink();
    }
    return 0;
}



poj 2728(Desert King) 最优比例生成树 (分数规划)