您的位置:首页 > 其它

【POJ 2728】[01分数规划]Desert King

2017-03-02 18:31 232 查看

题意

给出n,表示点数,对于每一个点给出三个属性x,y,h。

要求求出一颗生成树,使得边的费用和与长度和之比ΣaiΣbi最小。

边的费用就是ai=abs(hi−hj),长度为bi=(xi−xj)2+(yi−yj)2−−−−−−−−−−−−−−−−−−√

题目解析

这道题就是最小比值生成树,可以用01规划来实现

点此看01规划板题

只是这里的贪心不一样,我们先重新计算边权,因为我们要尽可能的让比值最小,也就是让生成树的权值最小要小于0才能让ans在当前区间,于是我们用Prim算法(不是Kruskal(要T)也不是Prime)求一发最小生成树。

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;

#define MAXN 1000
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;

template<class T>
void Read(T &x){
x=0;char c=getchar();bool flag=0;
while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
if(flag)x=-x;
}

const double EPS = 1e-4;
int Sign(const double x){
if(x>EPS)return 1;
else if(x<-EPS)return -1;
else return 0;
}

double a[MAXN+10][MAXN+10];
double b[MAXN+10][MAXN+10];
double d[MAXN+10][MAXN+10];
double dist[MAXN+10];
bool vis[MAXN+10];
int n;

double Prim(){
for(int i=1;i<=n;++i)dist[i]=1e18;
memset(vis,0,sizeof(vis));

dist[1]=0;

double ans=0;
int x;double dmin;
for(int i=1;i<=n;++i){
x=-1,dmin=1e18;
for(int j=1;j<=n;++j)
if(!vis[j]&&Sign(dmin-dist[j])>0)x=j,dmin=dist[j];
ans+=dmin;
vis[x]=1;
for(int j=1;j<=n;++j)
if(!vis[j]&&Sign(dist[j]-d[x][j])>0)dist[j]=d[x][j];
}

return ans;
}

bool check(double l){
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
d[i][j]=a[i][j]-l*b[i][j];

return Sign(Prim())<=0;
}

double x[MAXN+10],y[MAXN+10];
double h[MAXN+10];

int main(){
while(~scanf("%d",&n)&&n){
for(int i=1;i<=n;++i)
scanf("%lf%lf%lf",&x[i],&y[i],&h[i]);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
a[i][j]=fabs(h[i]-h[j]);
b[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}

double l=0,r=10000000,mid;
while(r-l>EPS){
mid=(l+r)/2.0;
if(check(mid))r=mid;
else l=mid;
}

printf("%0.3lf\n",l);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  01分数规划