http://poj.org/problem?id=2728
【参考】http://blog.sina.com.cn/s/blog_6af663940100lrz6.html
【题目大意】
国王为了给村庄供水需要铺水管,国王所在的村庄为起始点。给出村庄的三维坐标,若村庄i与j铺水管,则水管的距离等于i与j的水平距离,水管的花费等于i与j的垂直距离的绝对值。问怎么铺水管在所有村庄得到供水前提下使得总花费/总距离最小,输出最小值。
【解题思路】
先用二分+prim水过,果然数据很水...
Dinkelbach算法直接让原先的横坐标(r)移动到使得当前情况sigma(h-len)为0的点上,这样会比二分快很多。
完全图绝对不能用Kruscal啊...
【Dinkelbach算法】http://www.cnblogs.com/KirisameMarisa/p/4187637.html
【总结】
0/1分数规划,Dinkelbach算法的使用,很高效。
【important】【Code】Dinkelbach:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int M=1005;
const double eps=1e-6;
const double inf=1e7+10;
int n;
double len[M][M],h[M][M];
double dis[M],x[M],y[M],z[M];
bool vis[M];
double ans=inf,sum=inf;
double length(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void prim()
{
double toth,totlen,lenlen[M],height[M];
while(1)
{
memset(height,0,sizeof(height));
memset(lenlen,0,sizeof(lenlen));
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
{
dis[i]=inf;
}
dis[1]=0;
toth=totlen=sum=0;
while(1)
{
double Min=inf;
int k=-1;
for(int j=1; j<=n; j++)
{
if(!vis[j]&&dis[j]<Min)
{
Min=dis[j];
k=j;
}
}
if(k==-1)
break;
vis[k]=1;
sum+=dis[k];
totlen+=lenlen[k];//
toth+=height[k];//
for(int i=1; i<=n; i++)
{
double temp=h[k][i]-ans*len[k][i];
if(dis[i]>temp)
{
dis[i]=temp;
lenlen[i] = len[k][i];//
height[i] = h[k][i];//
}
}
}
ans=toth/totlen;//
if(abs(sum)<eps)
break;
}
}
int main()
{
while(~scanf("%d",&n)&&n)
{
for(int i=1; i<=n; i++)
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
len[j][i]=len[i][j]=length(x[i],y[i],x[j],y[j]);
h[i][j]=h[j][i]=abs(z[i]-z[j]);
}
}
prim();
printf("%.3f\n",ans);
}
}
【Code】shuibi二分:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int M=1005;
const double eps=1e-6;
const double inf=1e7+10;
int n;
double len[M][M],h[M][M];
double dis[M],x[M],y[M],z[M];
bool vis[M];
double length(double x1,double y1,double x2,double y2)
{
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double prim(double s)
{
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
{
dis[i]=inf;
}
dis[1]=0;
double sum=0;
while(1)
{
double Min=inf;
int k=-1;
for(int j=1; j<=n; j++)
{
if(!vis[j]&&dis[j]<Min)
{
Min=dis[j];
k=j;
}
}
vis[k]=1;
sum+=dis[k];
if(k==-1)
break;
for(int i=1; i<=n; i++)
{
double temp=h[k][i]-s*len[k][i];
if(dis[i]>temp)
dis[i]=temp;
}
}
return sum;
}
int main()
{
while(~scanf("%d",&n)&&n)
{
for(int i=1; i<=n; i++)
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
for(int i=1; i<=n; i++)
{
for(int j=i+1; j<=n; j++)
{
len[j][i]=len[i][j]=length(x[i],y[i],x[j],y[j]);
h[i][j]=h[j][i]=abs(z[i]-z[j]);
}
}
double l=0,r=inf;
while(r-l>eps)
{
double mid=(l+r)/2.0;
double temp=prim(mid);
// printf("%f %f %f %f\n",l,r,mid,temp);
if(temp>0)
l=mid;
else
r=mid;
}
printf("%.3f\n",l);
}
}