2019 UESTC ACM Training for Graph[O] (最优比例生成树)

题意:空间中内有n个点,连n-1条边,使得这些边 的高度差之和比上水平距离之和最小。

最优比例生成树问题
对于一个最小生成树,可以表达为如下形式:
x1 * a1+x2 * a2+…+xm * am
其中x1 ~ xm要么为1要么为0,并且xi的和为n-1。ai是编号为i的边的权值,并且选择的点要使得图联通。
那么对于题中的问题,取一个生成树满足sum{xi * hi}/sum{xi * di}>=ans
对上式变形,得到:
sum{xi * (hi-ans * di)}>=0
很显然,这就是普通的最小生成树的形式,每条边的边权为hi-ans * di,并且由于ans有单调性,二分ans然后求最小生成树,然后判断最小生成树的和是否大于零,然后卡左右边界的值即可。
但是二分的方法会超时。因此考虑使用迭代的方法解决。
假设当前讨论到的答案是x,那么每条边的边权为hi-x * di。用变量x0记录之前算出的答案x,然后跑最小生成树,记录sumh,sumd为这次最小生成树中高度差的和以及水平距离的和,然后用sumh/sumd当作新的x值进行迭代计算。直到x和x0相比不再变小,就可以停止迭代了。
这种算法的正确性建立在x的每次迭代和上次相比会单调递减。更具体的分析参考博客:https://blog.csdn.net/chenzhenyu123456/article/details/48160209

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const int maxn=1005;
const double inf=1e10;
const double eps=1e-4;
inline void _read(int &x){
    char t=getchar();bool sign=true;
    while(t<'0'||t>'9')
    {if(t=='-')sign=false;t=getchar();}
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
    if(!sign)x=-x;
}
int n,fa[maxn];
double x[maxn],y[maxn],z[maxn];
int getfa(int a){return a==fa[a]?a:fa[a]=getfa(fa[a]);}
struct Edge{
	int from,to;
	double d,h,dis;
	Edge(int from,int to,double d,double h,double dis):from(from),to(to),d(d),h(h),dis(dis){}
	bool operator <(const Edge& red)const{
		return dis < red.dis;
	}
};
vectoredges;
void addedges(int from,int to,double d,double h){
	edges.push_back(Edge(from,to,d,h,0));
}
double get_dist(int i,int j){
	return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main(){
	_read(n);
	for(int i=1;i<=n;i++)cin>>x[i]>>y[i]>>z[i];
	for(int i=1;i

 

你可能感兴趣的:(树)