题目描述
大卫大帝刚刚成为一个沙漠国家的国王。为了赢得人民的尊重,他决定在全国各地修建渠道,为每个村庄供水。与他的首都村庄相连的村庄将被浇灌。作为国家的主宰者和智慧的象征,他需要以最优雅的方式构建通道。
经过几天的研究,他终于想出了自己的计划。他希望将每英里通道的平均成本降至最低。换句话说,通道的总成本与总长度的比率必须最小化。他只需要建立必要的渠道,将水输送到所有村庄,这意味着将每个村庄连接到首都只有一种方法。
他的工程师们勘察了整个国家并记录了每个村庄的位置和高度。所有通道必须直通两个村庄之间,并水平建造。由于每两个村庄的海拔高度不同,他们得出结论,两个村庄之间的每个通道都需要一个垂直的提水器,可以提水或让水流下来。通道的长度是两个村庄之间的水平距离。通道的成本是升降机的高度。你应该注意到,每个村庄在不同的高度,不同的频道不能共用一个升降机。渠道可以安全相交,没有三个村庄在同一条线上。
作为大卫王的首席科学家和程序员,您需要找出构建渠道的最佳解决方案。
题意简介
给出 n个点的坐标 和海拔,任意两点连得边的长度为这两个点的直线距离(勾股定理),边的花费为海拔之差,希望找一棵生成树,并最小化
∑
i
=
1
n
−
1
c
o
s
t
[
i
]
∑
i
=
1
n
−
1
d
i
s
[
i
]
\frac{\sum_{i=1}^{n-1}{cost[i]}}{\sum_{i=1}^{n-1}{dis[i]}}
∑i=1n−1dis[i]∑i=1n−1cost[i]
其中
i
i
i 表示生成树每一条边,
c
o
s
t
[
i
]
cost[i]
cost[i]表示第
i
i
i 条边的花费,
d
i
s
[
i
]
dis[i]
dis[i] 表示第
i
i
i 条边的长度
解题思路
考虑 0/1分数规划
因为本蒟蒻不会Dinkelbach,所以只能写二分
实数二分一个答案
w
w
w ,看是否能有一种生成树,使
∑
i
=
1
n
−
1
c
o
s
t
[
i
]
∑
i
=
1
n
−
1
d
i
s
[
i
]
≤
w
\frac{\sum_{i=1}^{n-1}{cost[i]}}{\sum_{i=1}^{n-1}{dis[i]}}\leq{w}
∑i=1n−1dis[i]∑i=1n−1cost[i]≤w
∑
i
=
1
n
−
1
c
o
s
t
[
i
]
≤
w
×
∑
i
=
1
n
−
1
d
i
s
[
i
]
\sum_{i=1}^{n-1}{cost[i]}\leq{w}\times\sum_{i=1}^{n-1}{dis[i]}
i=1∑n−1cost[i]≤w×i=1∑n−1dis[i]
∑
i
=
1
n
−
1
c
o
s
t
[
i
]
−
w
×
∑
i
=
1
n
−
1
d
i
s
[
i
]
≤
0
\sum_{i=1}^{n-1}{cost[i]}-{w}\times\sum_{i=1}^{n-1}{dis[i]}\leq{0}
i=1∑n−1cost[i]−w×i=1∑n−1dis[i]≤0
也就是把每一条边变成
c
o
s
t
[
i
]
−
w
×
d
i
s
[
i
]
cost[i]-{w}\times{dis[i]}
cost[i]−w×dis[i]
然后求出最小生成树,如果边权和小于0,就调小w,否则就调大
PS:因为任意两点都可以连边,因此这是一个完全图,所以还是
P
r
i
m
Prim
Prim跑的快一点
代码如下
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
double cost[N][N];
double len[N][N];
double dis[N];
int x[N],y[N],h[N];
bool vis[N];
int n;
double calc(int a,int b)
{
return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
inline int read()
{
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
if(flag) return X;
return ~(X-1);
}
bool check(double w)
{
double sum=0.000;
for(int i=1;i<=n;i++)
{
dis[i]=cost[1][i]-len[1][i]*w;
}
memset(vis,0,sizeof(vis));
vis[1]=1;
for(int t=2;t<=n;t++)
{
double minx=1e9+7;
int pos=-1;
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
if(dis[i]<minx)
{
minx=dis[i];
pos=i;
}
}
vis[pos]=1;
sum+=minx;
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
if(dis[i]>cost[pos][i]-len[pos][i]*w)
{
dis[i]=cost[pos][i]-len[pos][i]*w;
}
}
}
if(sum-0<0.000) return 1;
return 0;
}
int main()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
while(n=read())
{
for(int i=1;i<=n;i++)
{
x[i]=read();
y[i]=read();
h[i]=read();
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
len[j][i]=len[i][j]=calc(i,j);
cost[j][i]=cost[i][j]=fabs(h[i]-h[j]);
}
}
double l=0.000,r=100.000,mid,ans;
while(r-l>1e-6)
{
mid=(l+r)/2;
if(check(mid))
{
ans=mid;
r=mid;
}
else l=mid;
}
printf("%.3lf\n",ans);
}
return 0;
}