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;
}
				
时间: 2024-10-11 09:59:22

poj 2728(Desert King) 最优比例生成树 (分数规划)的相关文章

POJ 2728 Desert King 最优比例生成树

最优比例生成树,迭代方法..... Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 21086   Accepted: 5916 Description David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels

ZOJ 2728 Desert King(最优比例生成树)

Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20586   Accepted: 5777 Description David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his coun

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

题目地址:POJ 2728 01分数规划的应用之一-最优比率生成树. 跟普通的01分数规划类似,只是这题的验证函数改成了最小生成树来验证.弱用的迭代法. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map>

POJ 2728 Desert King 最优比率生成树

Desert King Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20978   Accepted: 5898 [Description] David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his co

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

http://poj.org/problem?id=2728 大致题意:有n个村庄,输入每个村庄的位置和高度,这n个村庄要连在一起,村与村之间的长度为他们之间的欧几里得距离,花费是两村之间的高度差,要求连在一起的花费和与距离和之比的最小值. 思路:明显的最优比率生成树.二分答案λ,每条边重新赋权c[i] - λd[i] ,因为要求比值最小,那么对于所有的生成树,它们的f[λ]必须>=0,所以只需求得基于最小生成树的f'[λ],当f'[λ] = 0时即找到了正解λ*. 二分: #include <

poj 2728 Desert King(最小比率生成树,迭代法)

引用别人的解释: 题意:有n个村庄,村庄在不同坐标和海拔,现在要对所有村庄供水,只要两个村庄之间有一条路即可, 建造水管距离为坐标之间的欧几里德距离(好象是叫欧几里德距离吧),费用为海拔之差 现在要求方案使得费用与距离的比值最小 很显然,这个题目是要求一棵最优比率生成树, 概念 有带权图G, 对于图中每条边e[i], 都有benifit[i](收入)和cost[i](花费), 我们要求的是一棵生成树T, 它使得 ∑(benifit[i]) / ∑(cost[i]), i∈T 最大(或最小). 这

【POJ2728】Desert King 最优比率生成树

题目大意:给定一个 N 个点的无向完全图,边有两个不同性质的边权,求该无向图的一棵最优比例生成树,使得性质为 A 的边权和比性质为 B 的边权和最小. 题解:要求的答案可以看成是 0-1 分数规划问题,即:选定一个数 mid,每次重新构建边权为 \(a[i]-mid*b[i]\) 的图,再在图上跑一遍最小生成树(这里由于是完全图,应该采用 Prim 算法)判断最小值和给定判定的最小值的关系即可,这里为:若最小值大于 mid,则下界提高,否则上界下降. 代码如下 #include<cmath>

poj 2728 Desert King 参数搜索解最优比例生成树

题意: 图中每条边有两个权值(cost,len),求其一棵生成树,使sum(cost)/sum(len)最小. 分析: 转化为求边权为s0*len-cost的最大生成树+牛顿迭代.s0为具有单调性迭代系数. 代码: //poj 2728 //sep9 #include <iostream> #include <cmath> #include <vector> using namespace std; const int maxN=1024; struct P { int

[POJ 2728]Desert King(0-1分数规划/最优比率生成树)

Description David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be