该专题主要是学会模拟退火这个玄学算法,我会在另一篇博客详细介绍模拟退火。
随机算法嘛,难免会错个几次,交的时候就是一把梭,wa了就wa了。
A - Run Away
给出n个点,求距所有点的最小距离最大的那个点的坐标。
没啥好说的,直接上模版。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<time.h>
using namespace std;
const int maxn=1005;
const double inf=1e10;
const double eps=1e-6;
const double pi=acos(-1.0);
double px[maxn],py[maxn];
double d[maxn];
double cx[maxn],cy[maxn];
double dist(double x1,double y1,double x2,double y2)
{
double x=x1-x2;
double y=y1-y2;
return sqrt(x*x+y*y);
}
int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
srand(time(NULL));
while(T--)
{
int R,C,M;
scanf("%d %d %d",&R,&C,&M);
double x,y;
for(int i=1; i<=M; i++)
{
scanf("%lf %lf",&x,&y);
px[i]=x,py[i]=y;
}
for(int i=1; i<=30; i++)
{
cx[i]=(rand()%1000+1)*1.0/1000*R;
cy[i]=(rand()%1000+1)*1.0/1000*C;
double thed=inf;
for(int j=1; j<=M; j++)
thed=min(thed,dist(cx[i],cy[i],px[j],py[j]));
d[i]=thed;
}
double hot=max(R,C)/(sqrt(1.0*M));
while(hot>eps)
{
for(int i=1; i<=30; i++)
{
for(int k=1; k<=30; k++)
{
x=cx[i],y=cy[i];
double tmp=rand()%10000*1.0/10000*10*pi;
double dx=x+hot*cos(tmp);
double dy=y+hot*sin(tmp);
if(dx>R||dx<0||dy>C||dy<0)
continue;
double thed=inf;
for(int j=1; j<=M; j++)
thed=min(thed,dist(dx,dy,px[j],py[j]));
if(d[i]<thed)
{
d[i]=thed;
cx[i]=dx;
cy[i]=dy;
}
}
}
hot*=0.8;
}
double ansd=-1;
int idx;
for(int i=1; i<=30; i++)
if(ansd<d[i])
{
ansd=d[i];
idx=i;
}
printf("The safest point is (%.1lf, %.1lf).\n",cx[idx],cy[idx]);
}
return 0;
}
B - A Star not a Tree?
给出n个点,求一个与这n个点的距离和最小的点。
和上面一样。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<time.h>
using namespace std;
const int maxn=105;
const double eps=1e-6;
const double inf=1e10;
const double pi=acos(-1.0);
int px[maxn],py[maxn];
double cx[maxn],cy[maxn];
double d[maxn];
double dis(double x1,double y1,double x2,double y2)
{
double x=x1-x2;
double y=y1-y2;
return sqrt(x*x+y*y);
}
int main()
{
//freopen("input.txt","r",stdin);
srand(time(NULL));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d %d",&px[i],&py[i]);
for(int i=1;i<=30;i++)
{
cx[i]=rand()%10001*1.0/10000*10000;
cy[i]=rand()%10001*1.0/10000*10000;
double thed=0;
for(int j=1;j<=n;j++)
thed+=dis(cx[i],cy[i],px[j],py[j]);
d[i]=thed;
//cout<<d[i]<<endl;
}
double hot=10000/sqrt(1.0*n);
while(hot>eps)
{
for(int i=1;i<=30;i++)
{
double x=cx[i],y=cy[i];
for(int j=1;j<=30;j++)
{
double tmp=rand()%10001*1.0/10000*10*pi;
double dx=hot*cos(tmp);
double dy=hot*sin(tmp);
double tmpx=x+dx;
double tmpy=y+dy;
if(tmpx<0||tmpx>10000||tmpy<0||tmpy>10000)continue;
double tmpd=0;
for(int k=1;k<=n;k++)
{
tmpd+=dis(tmpx,tmpy,px[k],py[k]);
//cout<<tmpd<<endl;
}
if(d[i]>tmpd)
{
cx[i]=tmpx;
cy[i]=tmpy;
d[i]=tmpd;
}
}
}
hot*=0.8;
}
double ans=inf;
for(int i=1;i<=30;i++)
{
ans=min(ans,d[i]);
}
printf("%.0f\n",ans);
}
C - Empire Strikes Back
给出一个圆形区域和n个点,求与这n个点的最小的距离最大的那个点。
以下是摘录特判
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<time.h>
using namespace std;
const int maxn=305;
const double inf=1e18;
const double eps=1e-6;
const double pi=acos(-1.0);
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x>0)return 1;
return -1;
}
struct Point
{
double x,y;
double len;
Point(double _x,double _y):x(_x),y(_y){len=sqrt(x*x+y*y);}
Point(){}
void read()
{
scanf("%lf %lf",&x,&y);
len=sqrt(x*x+y*y);
}
}p[maxn];
int N,R;
double dis(Point a,Point b)
{
double x=a.x-b.x;
double y=a.y-b.y;
return sqrt(x*x+y*y);
}
double F(Point a)
{
double res=inf;
for(int i=1;i<=N;i++)
res=min(res,dis(a,p[i]));
return res;
}
double Rand(double num)
{
return (rand()%10001+0.0)/10000*num;
}
bool OutCircle(double x,double y)
{
return sgn(x*x+y*y-R*R)>0;
}
double solve()
{
int times=15;
double res=0;
while(times--)
{
double tmpmax;
double hot=R;
double x=Rand(R),y=Rand(R);
while(OutCircle(x,y))
x=Rand(R),y=Rand(R);
tmpmax=F(Point(x,y));
while(hot>eps)
{
for(int t=1;t<=15;t++)
{
double tmp=Rand(1)*10*pi;
double dx=hot*cos(tmp);
double dy=hot*sin(tmp);
double cx=x+dx,cy=y+dy;
if(OutCircle(cx,cy))continue;
double tmpdis=F(Point(cx,cy));
if(tmpdis>tmpmax)
{
tmpmax=tmpdis;
x=cx,y=cy;
}
}
hot*=0.9;
}
res=max(res,tmpmax);
}
return res;
}
int main()
{
//freopen("input.txt","r",stdin);
srand(time(NULL));
scanf("%d %d",&N,&R);
for(int i=1;i<=N;i++)
p[i].read();
double ans=0;
for(int i=1;i<=N;i++)
{
if(sgn(p[i].len)==0)continue;
double tmpx=1.0*p[i].x/p[i].len*R;
double tmpy=1.0*p[i].y/p[i].len*R;
Point tmp=Point(tmpx,tmpy);
ans=max(ans,F(tmp));
tmp=Point(-tmpx,-tmpy);
ans=max(ans,F(tmp));
}
//cout<<ans<<endl;
for(int i=1;i<=N;i++)
for(int j=i+1;j<=N;j++)
{
if(sgn(p[i].x-p[j].x)!=0||sgn(p[i].y-p[j].y)!=0)
{
if(sgn(p[i].y-p[j].y)==0)
{
double tmpx=(p[i].x+p[j].x)/2;
double tmpy=sqrt(R*R-tmpx*tmpx);
Point tmp=Point(tmpx,tmpy);
ans=max(ans,F(tmp));
tmp=Point(tmpx,-tmpy);
ans=max(ans,F(tmp));
}
else
{
double k=-(p[j].x-p[i].x+0.0)/(p[j].y-p[i].y);
double x=(p[i].x+p[j].x+0.0)/2;
double y=(p[i].y+p[j].y+0.0)/2;
double b=y-k*x;
double delta=4*k*k*b*b-4*(k*k+1)*(b*b-R*R);
double tmpx=(-2*k*b+sqrt(delta))/(2*(k*k+1));
double tmpy=k*tmpx+b;
Point tmp=Point(tmpx,tmpy);
ans=max(ans,F(tmp));
tmpx=(-2*k*b-sqrt(delta))/(2*(k*k+1));
tmpy=k*tmpx+b;
tmp=Point(tmpx,tmpy);
ans=max(ans,F(tmp));
//cout<<ans<<endl;
}
}
}
ans=max(ans,solve());
printf("%.5f\n",ans);
}
D - Super Star
找到一个点,使得以这个点为球心,作一个能包围所有点的球,并且半径最小。
实际上还是求与这n个点的最远距离最小的坐标,然而不同的是这次是三维坐标,用上面的方法进行随机总是会wa,所以得增加一点贪心的思想,尽量与距当前点最远的那个点的方向走。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<time.h>
using namespace std;
const int maxn=35;
const double eps=1e-9;
const double pi=acos(-1.0);
const double inf=1e10;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x>0)return 1;
return -1;
}
struct Point3
{
double x,y,z;
Point3(double _x,double _y,double _z):x(_x),y(_y),z(_z) {}
Point3() {}
void read()
{
scanf("%lf %lf %lf",&x,&y,&z);
}
};
double Rand(int num)
{
return rand()%10001*1.0/10000*num;
}
double dis(Point3 a,Point3 b)
{
double x=a.x-b.x;
double y=a.y-b.y;
double z=a.z-b.z;
return sqrt(x*x+y*y+z*z);
}
Point3 p[maxn];
int n;
void solve()
{
int times=10;
double ans=inf;
while(times--)
{
double hot=150;
double x=Rand(100),y=Rand(100),z=Rand(100);
double tmpmin=0;
for(int i=1;i<=n;i++)
tmpmin=max(tmpmin,dis(Point3(x,y,z),p[i]));
while(hot>eps)
{
double maxdis=-1;
int idx;
for(int i=1;i<=n;i++)
{
double thedis=dis(Point3(x,y,z),p[i]);
if(sgn(maxdis-thedis)<0)
maxdis=thedis,idx=i;
}
x=x+(p[idx].x-x)/maxdis*hot;
y=y+(p[idx].y-y)/maxdis*hot;
z=z+(p[idx].z-z)/maxdis*hot;
hot*=0.99;
}
double tmpmin2=0;
for(int i=1;i<=n;i++)
tmpmin2=max(tmpmin2,dis(Point3(x,y,z),p[i]));
tmpmin=min(tmpmin,tmpmin2);
ans=min(ans,tmpmin);
}
printf("%.5lf\n",ans+eps);
}
int main()
{
srand(time(NULL));
//freopen("input.txt","r",stdin);
while(~scanf("%d",&n)&&n)
{
for(int i=1; i<=n; i++)
p[i].read();
solve();
}
}
E - Texas Trip
求能包围n个点的最小正方形。
按照题目要求求正方形的最小面积,如果正方形的一边平行于x轴,那么其面积就是x方向上的距离和y方向的距离中最大的那个平方。如果不是平行于x轴,那我们就得计算那个方向和与那个方向垂直的方向上的最大值。此时我们就会想到坐标旋转。
既然是最小值,那么面积公式显然有一个单调减的过程,并且旋转的性质能保证面积公式是一个周期函数,所以显然得到它是有一个单峰的函数。此时我们会想到三分。
注意,三分的精度要求很高。。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
const int maxn=35;
const double eps=1e-9;
const double pi=acos(-1.0);
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x>0)return 1;
return -1;
}
struct Point
{
double x,y;
Point(double sx,double sy):x(sx),y(sy){}
Point(){}
void read()
{
scanf("%lf %lf",&x,&y);
}
}p[maxn];
int n;
double f(double num)
{
double maxx=-1000,maxy=-1000,minx=1000,miny=1000;
for(int i=1;i<=n;i++)
{
double x=p[i].x*cos(num)-p[i].y*sin(num);
double y=p[i].x*sin(num)+p[i].y*cos(num);
if(sgn(maxx-x)<0)maxx=x;
if(sgn(maxy-y)<0)maxy=y;
if(sgn(minx-x)>0)minx=x;
if(sgn(miny-y)>0)miny=y;
}
return max(maxx-minx,maxy-miny);
}
int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
p[i].read();
double L=0,R=pi/2;
while(sgn(L-R)<0)
{
double mid1=(L+R)/2;
double mid2=(mid1+R)/2;
if(sgn(f(mid1)-f(mid2))<0)
R=mid2;
else L=mid1;
}
double ans=f(L);
printf("%.2f\n",ans*ans);
}
return 0;
}
F - Point of view in Flatland
题意:给出三个圆,让你求出一个点,要求该点与圆作出两条切线之间的角度相等。
一开始没看懂题意,于是就去翻题解了,结果无意看到判断函数使用角度的方差来计算。。
中间随机的方式我用之前的做法写了,不管怎么改都会wa。。看了题解才学会另一种方式。只能说模拟退火真玄学。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<time.h>
#include<math.h>
using namespace std;
const double eps=1e-7;
const double inf=1e10;
const double pi=acos(-1.0);
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x>0)return 1;
return -1;
}
struct Circle
{
double x,y,r;
Circle(double _x,double _y,double _r):x(_x),y(_y),r(_r){}
Circle(double _x,double _y):x(_x),y(_y){}
Circle(){}
void read()
{
scanf("%lf %lf %lf",&x,&y,&r);
}
};
Circle C[5];
typedef Circle Point;
double dis(Point a,Point b)
{
double x=a.x-b.x;
double y=a.y-b.y;
return sqrt(x*x+y*y);
}
double F(Point a)
{
double tmp[4];
for(int i=1;i<=3;i++)
tmp[i]=C[i].r/dis(a,C[i]);
double sum=tmp[1]+tmp[2]+tmp[3];
sum/=3;
double res=0;
for(int i=1;i<=3;i++)
res+=sqrt(10*(sum-tmp[i])*(sum-tmp[i]));
//cout<<res/3<<endl;
return res;
}
double Rand(int num)
{
return rand()%10001*1.0/10000*num;
}
Point e1,e2;
double Rang2(double a,double b)
{
return a+Rand(1)*(b-a);
}
void solve()
{
int times=15;
double ans=inf;
double ansx,ansy;
while(times--)
{
double x=Rang2(e1.x,e2.x),y=Rang2(e1.y,e2.y);
double value=F(Point(x,y));
double hot=max(e2.x-e1.x,e2.y-e1.y);
while(hot>eps)
{
for(int k=1;k<=15;k++)
{
//double tmp=Rand(1)*10*pi;
//double dx=hot*cos(tmp),dy=hot*sin(tmp);
double cx=Rang2(max(e1.x,x-hot),min(e2.x,x+hot));
double cy=Rang2(max(e1.y,y-hot),min(e2.y,y+hot));
double itF=F(Point(cx,cy));
if(sgn(itF-value)<0)
{
value=itF;
x=cx;
y=cy;
}
}
hot*=0.8;
}
if(sgn(ans-value)>0)
{
ans=value;
ansx=x;
ansy=y;
}
}
if(sgn(ans))
puts("No solution");
else printf("%.2f %.2f\n",ansx,ansy);
}
bool read()
{
e1.x=e1.y=inf;
e2.x=e2.y=-inf;
bool flag=0;
for(int i=1;i<=3;i++)
{
C[i].read();
if(C[i].x!=0||C[i].y!=0||C[i].r!=0)flag=1;
if(sgn(e1.x-C[i].x)>0)e1.x=C[i].x;
if(sgn(e1.y-C[i].y)>0)e1.y=C[i].y;
if(sgn(e2.x-C[i].x)<0)e2.x=C[i].x;
if(sgn(e2.y-C[i].y)<0)e2.y=C[i].y;
}
return flag;
}
int main()
{
srand(time(NULL));
//freopen("input.txt","r",stdin);
while(read())
solve();
}
G - Geometrical dreams
这题很显然随机找到第一个点,然后按照题意要求算出其余的点的坐标,并以最后一个点再推出第一个点,用两个第一个点的坐标距离来当作判断函数。
然而我还是不管怎么改还是wa。。结果发现是精度问题,我写的时候看到discuss上说输出用%.01输出。。结果我改成%.0f就过了。。所以discuss上也不一定对的。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<time.h>
using namespace std;
const double inf=1e10;
const int maxn=55;
const double eps=1e-7;
const double pi=acos(-1.0);
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x>0)return 1;
return -1;
}
struct Point
{
double x,y;
Point(double _x,double _y):x(_x),y(_y){}
Point(){}
void read()
{
scanf("%lf %lf",&x,&y);
}
void out()
{
cout<<x<<' '<<y<<endl;
}
Point operator-(const Point &b)const
{
return Point(x-b.x,y-b.y);
}
Point operator+(const Point &b)const
{
return Point(x+b.x,y+b.y);
}
double operator*(const Point &b)const
{
return x*b.x+y*b.y;
}
double operator&(const Point &b)const
{
return x*b.y-y*b.x;
}
}p[maxn];
double angle[maxn];
Point e1,e2;
double Rand(int num)
{
return rand()%10001*1.0/10000*num;
}
double Range(double a,double b)
{
return a+Rand(1)*(b-a);
}
Point ans[maxn];
Point Rotate(Point a,double ang)
{
double x=a.x*cos(ang)-a.y*sin(ang);
double y=a.x*sin(ang)+a.y*cos(ang);
return Point(x,y);
}
int n;
void solve2(Point a)
{
ans[1]=a;
for(int i=1;i<=n;i++)
{
Point tmp=ans[i]-p[i];
tmp=Rotate(tmp,angle[i]);
tmp=tmp+p[i];
ans[i+1]=tmp;
}
}
double dis(Point a,Point b)
{
double x=a.x-b.x;
double y=a.y-b.y;
return sqrt(x*x+y*y);
}
double F(Point a)
{
solve2(a);
return dis(a,ans[n+1]);
}
void solve()
{
int times=15;
double res=inf;
double ansx,ansy;
while(times--)
{
double hot=max(e2.x-e1.x,e2.y-e1.y);
double x=Range(e1.x,e2.x);
double y=Range(e1.y,e2.y);
double MinF=F(Point(x,y));
while(hot>eps)
{
for(int k=1;k<=15;k++)
{
double tmpx=Range(max(e1.x,x-hot),min(e2.x,x+hot));
double tmpy=Range(max(e1.y,y-hot),min(e2.y,y+hot));
double itF=F(Point(tmpx,tmpy));
if(sgn(itF-MinF)<0)
{
MinF=itF;
x=tmpx;
y=tmpy;
}
}
hot*=0.9;
}
if(sgn(res-MinF)>0)
{
res=MinF;
ansx=x;
ansy=y;
}
}
solve2(Point(ansx,ansy));
for(int i=1;i<=n;i++)
printf("%.0f %.0f\n",ans[i].x,ans[i].y);
//tmp.out();
//cout<<cos(angle[1])<<endl;
//cout<<sin(angle[1])<<endl;
}
int main()
{
//freopen("input.txt","r",stdin);
srand(time(NULL));
e1.x=e1.y=inf;
e2.x=e2.y=-inf;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
p[i].read();
//p[i].out();
if(sgn(e1.x-p[i].x)>0)e1.x=p[i].x;
if(sgn(e1.y-p[i].y)>0)e1.y=p[i].y;
if(sgn(e2.x-p[i].x)<0)e2.x=p[i].x;
if(sgn(e2.y-p[i].y)<0)e2.y=p[i].y;
}
for(int i=1;i<=n;i++)
{
scanf("%lf",&angle[i]);
angle[i]=angle[i]/180*pi;
}
solve();
}
实习搞得我好tm烦啊。。
垃圾湖工计算机学院大家不要报考。。