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

题目:有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可,建造水管距离为坐标之间的欧几里德距离,费用为海拔之差,现在要求方案使得费用与距离的比值最小。

思路:和普通01分数规划相同,二分或者迭代来求,建最小生成树时需要用prime算法(点数较少,边数很多,完全图)。

二分代码:

/*2204ms*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define inf 1e6
#define eps 1e-8
const int maxn=1010;
int n;
int x[maxn],y[maxn],h[maxn];
double dis[maxn][maxn],cost[maxn][maxn];
double dist(int i,int j)
{
    return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j]));
}
bool visited[maxn];
double w[maxn][maxn],lowcost[maxn];
int prime(double x)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            w[i][j]=cost[i][j]-dis[i][j]*x;

    for(int i=1;i<=n;i++)
        lowcost[i]=w[1][i];

    memset(visited, false, sizeof(visited));
    lowcost[1]=0;
    visited[1]=true;
    double ans=0;
    for(int i=1;i<n;i++)
    {
        double mi=inf;
        int k;
        for(int j=1;j<=n;j++)
        if(!visited[j]&&mi>lowcost[j])
        {
            mi=lowcost[j];
            k=j;
        }
        visited[k]=true;
        ans+=mi;
        for(int j=1;j<=n;j++)
        {
            if(!visited[j]&&lowcost[j]>w[k][j])
                lowcost[j]=w[k][j];
        }
    }
    return ans<=0;
}

double solve()
{
    double l=0,r=inf;
    while(r-l>eps)
    {
        double mid=(l+r)/2.0;
        if(prime(mid))
            r=mid;
        else l=mid;
    }
    return l;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&x[i],&y[i],&h[i]);
        int m=0;
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            dis[i][j]=dis[j][i]=dist(i,j);
            cost[i][j]=cost[j][i]=1.0*abs(h[i]-h[j]);
        }
        printf("%.3f\n",solve());
    }
    return 0;
}

Dinkelbach迭代(效率比较高) 代码:

/*226ms*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define inf 1e6
#define eps 1e-5
const int maxn=1010;
int n;
int x[maxn],y[maxn],h[maxn];
double dis[maxn][maxn],cost[maxn][maxn];
double dist(int i,int j)
{
    return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j])+1.0*(y[i]-y[j])*(y[i]-y[j]));
}

bool visited[maxn];
double w[maxn][maxn],lowcost[maxn];
int pre[maxn];
double prime(double x)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            w[i][j]=cost[i][j]-dis[i][j]*x;

    for(int i=1;i<=n;i++)
    {
        lowcost[i]=w[1][i];
        pre[i]=1;
    }

    memset(visited, false, sizeof(visited));
    lowcost[1]=0;
    visited[1]=true;
    double ans1=0,ans2=0;
    for(int i=1;i<n;i++)
    {
        double mi=inf;
        int k;
        for(int j=1;j<=n;j++)
        if(!visited[j]&&mi>lowcost[j])
        {
            mi=lowcost[j];
            k=j;
        }
        visited[k]=true;
        ans1+=cost[pre[k]][k];
        ans2+=dis[pre[k]][k];
        for(int j=1;j<=n;j++)
        {
            if(!visited[j]&&lowcost[j]>w[k][j])
            {
                lowcost[j]=w[k][j];
                pre[j]=k;
            }
        }
    }
    return ans1/ans2;
}

double solve()
{
    double ans=0,L;
    while(1)
    {
        L=ans;
        ans=prime(L);
        if(fabs(ans-L)<eps)
            break;
    }
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&x[i],&y[i],&h[i]);
        int m=0;
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            dis[i][j]=dis[j][i]=dist(i,j);
            cost[i][j]=cost[j][i]=1.0*abs(h[i]-h[j]);
        }
        printf("%.3f\n",solve());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpXFire/article/details/81479421
今日推荐