杭电多校-Map-(模拟退火)

Map

题意:
就是一个平面图上,有两个矩形,给你大矩形的四个点,然后小矩形的四个点。小矩形肯定在大矩形的里面。现在要你找一个点,这个点在小矩形的位置和大矩形的相对位置一样。
在这里插入图片描述
思考:
1.我感觉这个题目很有趣,这个p点确实很有趣的感觉。就是既然要让这个点在两个矩形的相对位置一样。比如给你p了,你怎么判断是否相对位置一样?其实可以根据比例,如果AD/ad = pA/pa = pB/pb = pC/pc = pD/pd。那么就固定的四个点的比例,这样就可以确定了。值得注意的是这里用大边除小边,这样误差小。其实用乘法误差更小,而且还能避免分母为0的情况。所以以后看分数的比例的时候,一定要转化为乘法再做。
2.现在就是如何找p的问题了,当时于说暴力找,就是每次枚举整个图的每个点找到比例最好的p点,然后再缩小范围再找。每次复杂度是n*
的把,n = 6000,m = 6000可以接受。但是查询有1e5次查询,所以肯定超时,就没法做了。
3.然后想了想这种找最优点的题目,就我之前做的模拟退火的题一样呀,模拟退火就是在图形上求最优点的算法。2018南京-Country Meow这道题就和本题几乎差不多。每次随机平面上的点作为p点,如果此时的p点的4个比例差最大的,也就是最不接近的那个还比我现在的p的最不接近的更优的画,那么我手里的p点就成为这个p点。也就是如果换成这个p更优的话就换。唯一要思考的地方就是求每个点的优度是多少。这样就可以了。一次模拟退火的复杂度差不多是3000。那么1e5*3000也是可以过的,但是超时了。因为退火里面用了exp这个函数,也就是求e的多少次方。这个函数就像log函数一样,复杂度很高,不能用太多次,如果要用的话,看看是否可以先预处理出来,或者重写,如果都不行,那么这个算法就不行了。之前这个题目2020威海-Clock Master也是卡log函数的复杂度,预处理出来就不超时了。
5.虽然这个做法是超时的,但是我感觉是个不错的练模拟退火的题目,如果查询次数没那么多,应该就可以过了。

代码:

#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define db double
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;

const int N = 2e5+10;

struct point{
	db x,y;
}A,B,C,D,a,b,c,d;

int T;
db n;
db ad,AD,bz;
db anwx,anwy,anw;

db get(point a,point b)
{
	db sum = sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
	return sum;
}

db f(db i,db j)
{
	db t1 = get(A,point{i,j});
	db tt1 = get(a,point{i,j});
	db t2 = get(B,point{i,j});
	db tt2 = get(b,point{i,j});
	db t3 = get(C,point{i,j});
	db tt3 = get(c,point{i,j});
	db t4 = get(D,point{i,j});
	db tt4 = get(d,point{i,j});
	db s1 = fabs(tt1*AD-t1*ad);
	db s2 = fabs(tt2*AD-t2*ad);
	db s3 = fabs(tt3*AD-t3*ad);
	db s4 = fabs(tt4*AD-t4*ad);
	db maxn = 0;
	maxn = max(maxn,s1);
	maxn = max(maxn,s2);
	maxn = max(maxn,s3);
	maxn = max(maxn,s4);
	return maxn;
}

void SA()
{
	db te = 3000,delt = 0.99,eps = 1e-15;
	db x = 0,y = 0,ans = 1e18;
	while(te>eps)
	{
		db xx = x+(rand()*2-RAND_MAX)*te;
		db yy = y+(rand()*2-RAND_MAX)*te;
		db ans_t = f(xx,yy);
		db pro = exp((ans-ans_t)/te)*RAND_MAX;
		if(ans>ans_t||pro>rand())
		{
			ans = ans_t;
			x = xx,y = yy;
		}
		te *= delt;
	}
	if(anw>ans)
	{
		anw = ans;
		anwx = x,anwy = y;
	}
}

signed main()
{
	IOS;
	srand(time(NULL));
	cin>>T;
	while(T--)
	{
		cin>>A.x>>A.y;
		cin>>B.x>>B.y;
		cin>>C.x>>C.y;
		cin>>D.x>>D.y;
		cin>>a.x>>a.y;
		cin>>b.x>>b.y;
		cin>>c.x>>c.y;
		cin>>d.x>>d.y;
		ad = get(a,d);AD = get(A,D);
		anw = 1e18;
		for(int i=1;i<=10;i++) SA();
		printf("%.8lf %.8lf\n",anwx,anwy);
	}
	return 0;
}

总结:
多多思考,多多联想以前的算法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值