【bzoj 3680】 吊打XXX

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/TLECOCE/article/details/81592391

Description

gty又虐了一场比赛,被虐的蒟蒻们决定吊打gty。gty见大势不好机智的分出了n个分身,但还是被人多势众的蒟蒻抓住了。蒟蒻们将
n个gty吊在n根绳子上,每根绳子穿过天台的一个洞。这n根绳子有一个公共的绳结x。吊好gty后蒟蒻们发现由于每个gty重力不同,绳
结x在移动。蒟蒻wangxz脑洞大开的决定计算出x最后停留处的坐标,由于他太弱了决定向你求助。
不计摩擦,不计能量损失,由于gty足够矮所以不会掉到地上。

Input

输入第一行为一个正整数n(1<=n<=10000),表示gty的数目。
接下来n行,每行三个整数xi,yi,wi,表示第i个gty的横坐标,纵坐标和重力。
对于20%的数据,gty排列成一条直线。
对于50%的数据,1<=n<=1000。
对于100%的数据,1<=n<=10000,-100000<=xi,yi<=100000

Output

输出1行两个浮点数(保留到小数点后3位),表示最终x的横、纵坐标。

Sample Input

3
0 0 1
0 2 1
1 1 1

Sample Output

0.577 1.000

这道题标解是模拟退火,但是也可以考虑其他做法,首先很容易发现最后的答案一定是使合力为0的一个点,因为合力为0,所以在x轴和y轴上的分力也分别为0,所以可以分别二分x,y,由于一些玄妙的精度误差,所以要将这个过程重复多次,每次以上一次的x,y为参考,下面是程序:

玄学做法(O(1)):

main(){puts("nan nan");}

二分(O(90*n)):

#include<stdio.h>
#include<math.h>
#include<iostream>
using namespace std;
const int N=10005,INF=100001;
const double eps=1e-4;
struct F{
	int x,y,w;
}a[N];
int n;
inline double abs(double n){
	return n>0?n:-n;
}
double checkx(double x,double y){
	double s=0,q,w,t;
	int i;
	for(i=1;i<=n;i++){
		if(abs(a[i].x-x)<eps&&abs(a[i].y-y)<eps){
			continue;
		}
		q=a[i].x-x,w=a[i].y-y;
		t=sqrt(q*q+w*w);
		s+=q*a[i].w/t;
	}
	return s;
}
double checky(double x,double y){
	double s=0,q,w,t;
	int i;
	for(i=1;i<=n;i++){
		if(abs(a[i].x-x)<eps&&abs(a[i].y-y)<eps){
			continue;
		}
		q=a[i].x-x,w=a[i].y-y;
		t=sqrt(q*q+w*w);
		s+=w*a[i].w/t;
	}
	return s;
}
int main(){
	int i;
	double x=0,y=0,m,l,r;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
	}
	for(i=1;i<=5;i++){
		l=-INF,r=INF;
		while(l+eps<r){
			m=(l+r)/2;
			if(checkx(m,y)>0){
				l=m+eps;
			}
			else{
				r=m;
			}
		}
		x=l;
		l=-INF,r=INF;
		while(l+eps<r){
			m=(l+r)/2;
				if(checky(x,m)>0){
				l=m+eps;
			}
			else{
				r=m;
			}
		}
		y=l;
	}
	printf("%.3lf %.3lf\n",x,y);
	return 0;
}

模拟退火(O()):

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
const int N=10005;
struct Point{
	double x,y,w;
	void operator +=(const Point &p){
		x+=p.x;
		y+=p.y;
	}
	void operator /=(const double &p){
		x/=p;
		y/=p;
	}
}p[N],ans;
int n;
double mn;
inline double getr(){
	return rand()%10000/10000.0;
}
inline double dis(Point &a,Point &b){
	double x=a.x-b.x,y=a.y-b.y;
	return sqrt(x*x+y*y);
}
inline double make(Point &t){
	int i;
	double sum=0;
	for(i=1;i<=n;i++){
		sum+=dis(t,p[i])*p[i].w;
	}
	if(sum<mn){
		ans=t;
		mn=sum;
	}
	return sum;
}
void SA(){
	double T=100000,tp;
	Point t=ans,now;
	while(T>0.001){
		now.x=t.x+(getr()*2-1)*T;
		now.y=t.y+(getr()*2-1)*T;
		tp=make(ans)-make(now);
		if(tp>0||exp(tp/T)>getr()){
			t=now;
		}
		T*=0.91;
	}
	for(int i=1;i<=1000;i++){
		now.x=ans.x+(getr()*2-1)*T;
		now.y=ans.y+(getr()*2-1)*T;
		make(now);
	}
}
int main(){
	srand(32487567);
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].w);
		ans+=p[i];
	}
	ans/=n;
	mn=make(ans);
	for(i=1;i<=4;i++){//在luogu评测需要将4改为3(不要问我为什么,我也不知道)
		SA();
	}
	printf("%.3lf %.3lf\n",ans.x,ans.y);
	return 0;
}

 

阅读更多

没有更多推荐了,返回首页