P1337 [JSOI2004]平衡点 / 吊打XXX(模拟退火)

如图:有n个重物,每个重物系在一条足够长的绳子上。每条绳子自上而下穿过桌面上的洞,然后系在一起。图中X处就是公共的绳结。假设绳子是完全弹性的(不会造成能量损失),桌子足够高(因而重物不会垂到地上),且忽略所有的摩擦。

问绳结X最终平衡于何处。

注意:桌面上的洞都比绳结X小得多,所以即使某个重物特别重,绳结X也不可能穿过桌面上的洞掉下来,最多是卡在某个洞口处。

输入格式

文件的第一行为一个正整数n(1≤n≤1000),表示重物和洞的数目。接下来的n行,每行是3个整数:Xi.Yi.Wi,分别表示第i个洞的坐标以及第 i个重物的重量。(-10000≤x,y≤10000, 0<w≤1000 )

输出格式

你的程序必须输出两个浮点数(保留小数点后三位),分别表示处于最终平衡状态时绳结X的横坐标和纵坐标。两个数以一个空格隔开。

输入输出样例

输入 #1复制

3
0 0 1
0 2 1
1 1 1

输出 #1复制

0.577 1.000

说明/提示

[JSOI]

题目链接:https://www.luogu.com.cn/problem/P1337

思路:经分析本质上这道题要求解的问题是找到一个点使得该点到其他重物距离乘相应重量的和最小典型的模拟退火问题。由于是初学该算法以及思想,之后会另开一篇博客整理笔记。

#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
#define maxn 10005
#define INF 2147483647
struct node{
	double x,y,val;
}a[maxn],now,ans,A;
int n;
double T;
double ans_min=1.0*1e18;

double dis(node a,node b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

double jud(node now){
	double res=0;
	for(int i=1;i<=n;i++)
		res+=a[i].val*dis(now,a[i]);
	if(res<ans_min) ans_min=res,ans=now;
	return res;
}

double Random(){ 
	return (double)rand()/RAND_MAX;
}

void work(double T){
	now=ans;
	while(T>0.001){
		A.x=now.x+T*(Random()*2-1);
		A.y=now.y+T*(Random()*2-1);
		double dE=jud(now)-jud(A);
		if(dE>0 || Random()<=exp(dE/T))
			now=A;
		T*=0.99;
	}
	for(int i=1;i<=1000;i++){
		A.x=ans.x+T*(Random()*2-1);
		A.y=ans.y+T*(Random()*2-1);
		jud(A);
	}
}

int main(void){

	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].val);
		ans.x+=a[i].x; ans.y+=a[i].y;
	}

	ans.x/=n; ans.y/=n;

	work(1000001);

	printf("%.3f %.3f\n",ans.x,ans.y);

	return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值