题目大意
给你很多点,两点之间包含收益值和成本值,你要找到一颗生成树,使得其各边的收益值和成本值得比值最大
解法
这是一道典型的0/1分数规划问题
我们有
∑
i
=
1
n
v
a
l
[
i
]
∑
i
=
1
n
c
o
s
t
[
i
]
≥
k
\frac{\sum^n_{i=1}val[i]}{\sum^n_{i=1}cost[i]}\geq k
∑i=1ncost[i]∑i=1nval[i]≥k
那么上述式子化简后其实可以是
∑
i
=
1
n
v
a
l
[
i
]
−
k
∗
c
o
s
t
[
i
]
≥
0
\sum^n_{i=1}{val[i]-k*cost[i]}\geq 0
i=1∑nval[i]−k∗cost[i]≥0
那么我们就可以而二分这个k,然后做一次生成树来判断最终累加的和是否大于等于0来判断其是否合法
代码如下
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-5;
struct Point
{
int x,y,z;
};
struct node
{
int x,y;
double w;
bool friend operator < (node a,node b)
{
return a.w<b.w;
}
};
node q[5000010];
Point point[1010];
double dist[1010][1010];
int high[1010][1010];
double mp[1010][1010];
int fa[1010],n;
inline double getdist(const Point &a,const Point &b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline double gethigh(const Point &a,const Point &b)
{
return abs(a.z-b.z)*1.0;
}
int find(int x)
{
return (x==fa[x])?(x):(fa[x]=find(fa[x]));
}
inline void init()
{
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
dist[i][j]=getdist(point[i],point[j]),
high[i][j]=gethigh(point[i],point[j]);
}
inline void build(const double mid)
{
for(int i=1; i<=n; i++)
fa[i]=i;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
mp[i][j]=mp[j][i]=high[i][j]-mid*dist[i][j];
}
inline bool check()
{
double flag=0;
int cnt=0;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
{
q[++cnt]=node{i,j,mp[i][j]};
}
sort(q+1,q+cnt+1);
int x,y;
double w;
for(int i=1;i<=cnt;i++)
{
x=q[i].x;
y=q[i].y;
w=q[i].w;
x=find(x);
y=find(y);
if(x==y)
continue;
fa[x]=fa[y];
flag+=w;
}
return flag>=0;
}
int main()
{
while(true)
{
scanf("%d",&n);
if(n==0)
break;
for(int i=1; i<=n; i++)
scanf("%d%d%d",&point[i].x,&point[i].y,&point[i].z);
init();
double l=0,r=1e5;
double mid;
while(r-l>eps)
{
mid=(l+r)/2.0;
build(mid);
if(check())
l=mid;
else
r=mid;
}
printf("%.3f\n",(l+r)/2.0);
}
return 0;
}