题解 P4636 【[SHOI2011]直线拟合】
CR_Raphael · · 题解
我怕不是个傻子
一道模版题肝模拟退火。
-孩子沉迷退火怎么办?逼他用模拟退火做计算几何再让他打正解,妈妈再也不用担心我的常数啦!
咳,说正解,旋转卡壳求凸包点边最长值,
不过朕的代码……我一开始想歪了……直接凸包+二分上了……似乎没见过这样搞的……
就是找凸包上每个边斜率在凸包另一侧的位置,就确定了离该边最远的点。其实如果用尺取法做的话,就等价于旋转卡壳了。
所以我独立发明了旋转卡壳?
贴个超级丑的代码,希望正统旋转卡壳亲之信之,则几何之隆,可计日而待也。
懒得做正解了,如果您是初学者,想用这篇代码为蓝本学旋转卡壳,那么我建议换一篇题解。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double pi = 3.1415926535;
const int maxn = 100005;
const double inf = 2000000001;
int n;
double k, l, ans;
int t;
struct pointt {
double x, y, tk;
} a[maxn], p[maxn];
int findd(double ff) {
int l, r, mid;
l=1; r=t;
while(l < r) {
mid=(l+r+1)/2;
if(a[mid].tk <= ff) l=mid;
else r=mid-1;
}
return l;
}
double count_ans(int kx) {
int i;
double kk=a[kx].tk, maxx, minn;
double kt;
if(kk < pi) kk+=pi;
else kk-=pi;
if((a[kx].x!= a[kx-1].x)) kt=(a[kx].y-a[kx-1].y)/(a[kx].x-a[kx-1].x);
else kt=inf;
i=findd(kk);
maxx=(kt*a[i].x-a[i].y)/sqrt(kt*kt+1);
minn=(kt*a[kx].x-a[kx].y)/sqrt(kt*kt+1);
if(kt == inf) {
maxx=a[i].x;
minn=a[kx].x;
}
return abs(maxx-minn)/2;
}
bool cmp(pointt a1, pointt a2) {
return a1.tk < a2.tk;
}
double count(pointt a1, pointt a2) {
double tt=atan2((a1.y-a2.y), (a1.x-a2.x));
if(tt < 0) tt+=2*pi;
return tt;
}
double ll(pointt a1, pointt a2) {
return sqrt((a1.y-a2.y)*(a1.y-a2.y) + (a1.x-a2.x)*(a1.x-a2.x));
}
int main() {
int i, minp, maxp;
double miny;
scanf("%d", &n);
for(i=1; i <= n; i++)
scanf("%lf%lf", &p[i].x, &p[i].y);
miny=p[1].y; minp=1;
for(i=1; i <= n; i++) {
if(p[i].y < miny) miny=p[i].y, minp=i;
}
swap(p[1], p[minp]);
for(i=1; i <= n; i++)
p[i].tk=atan2(p[i].y-p[1].y, p[i].x-p[1].x);
sort(p+1, p+1+n, cmp);
t=0; i=0;
do {
i++;
if(i == n+1) i=1;
while(t > 1 && (count(p[i], a[t]) < count(a[t], a[t-1]))) {
t--;
}
t++;
a[t]=p[i];
a[t].tk=count(a[t], a[t-1]);
} while(i != 1 || t <= 1);
miny=inf;
a[1].tk = -0.1;
for(i=2; i <= t; i++) {
miny=min(miny, count_ans(i));
}
printf("%.2lf\n", miny);
return 0;
}
另外呢,将军退火,性行淑均,适用于昔日,评测机称之曰90,愚以为此做题之法,仅供观赏,诸将切勿惫怠。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
using namespace std;
const int maxn = 100005;
const double maxl = 200000000;
const double pai = 3.14159;
const double delta=0.9;
int n;
double a[maxn], b[maxn], t;
double ansp, ansk;
int ts;
double count(double k) {
int i;
double ll, maxx=-maxl, minn=maxl;
for(i=1; i <= n; ++i) {
ll=k*a[i]-b[i];
minn=min(minn, ll);
maxx=max(maxx, ll);
}
maxx=maxx/sqrt(k*k+1);
minn=minn/sqrt(k*k+1);
return (maxx-minn)/2;
}
void SA() {
double kk=ansk;
double tk, newp;
t=1777;
while(t > 1e-14) {
tk=kk+(rand()*2.0-RAND_MAX)*1.0/10000*t;
newp=count(tan(tk));
//cout<<tk<<' '<<newp<<endl;
//cin>>ts;
if(newp < ansp) {
kk=tk;
ansk=tk;
ansp=newp;
}
else if(exp(-(newp-ansp)*1000/t)*RAND_MAX > rand()) {
kk=tk;
}
t*=delta;
}
return;
}
int main() {
srand(time(NULL));
scanf("%d", &n);
//cout<<RAND_MAX<<endl;
int i;
double pi, tt, stt;
for(i=1; i <= n; ++i) {
scanf("%lf%lf", &a[i], &b[i]);
}
//cout<<count(1)<<endl;
ansk=0;
ansp=count(tan(ansk));
for(pi=-1.6; pi <= 1.6; pi+=0.01) {
tt=count(tan(pi));
if(tt < ansp) {
ansp=tt;
ansk=pi;
}
}
stt=ansk;
for(pi=stt-0.1; pi <= stt+0.1; pi+=0.001) {
tt=count(tan(pi));
if(tt < ansp) {
ansp=tt;
ansk=pi;
}
}
stt=ansk;
for(pi=stt-0.01; pi <= stt+0.01; pi+=0.0001) {
tt=count(tan(pi));
if(tt < ansp) {
ansp=tt;
ansk=pi;
}
}
stt=ansk;
for(pi=stt-0.001; pi <= stt+0.001; pi+=0.00001) {
tt=count(tan(pi));
if(tt < ansp) {
ansp=tt;
ansk=pi;
}
}
SA();
printf("%.2lf\n", ansp);
return 0;
}