题意:
就是一个平面图上,有两个矩形,给你大矩形的四个点,然后小矩形的四个点。小矩形肯定在大矩形的里面。现在要你找一个点,这个点在小矩形的位置和大矩形的相对位置一样。
思考:
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;
}
总结:
多多思考,多多联想以前的算法。