Gym101158 J 三分 or 模拟退火 Cover the Polygon with Your Disk
@
Gym101158 J: 求圆与给定凸多边形最大面积交
传送门:点我点我
求 $10 $ 个点组成的凸多边形 $(convex\quad polygon) $ ,坐标范围 $[-100,100] $ ,与一个给定半径的圆的最大面积交。圆心的位置由你确定!
模拟退火坐标。
三分套三分求最优坐标,坑点是第二个三分的上下限需要动态求。
Steepest descent
Downhill simplex (Nelder–Mead)
Quasi-Newton (BFGS)
Evolution strategy (CMA-ES)
模拟退火
抄了个这场$Gym\quad rank1 $的模拟退火方法。
#include<bits/stdc++.h>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
#define pb push_back
#define o2(x) (x)*(x)
#define db double
using namespace std;
typedef long long LL;
typedef pair<int, LL> pii;
#define cross(p1,p2,p3) ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y))
#define crossOp(p1,p2,p3) sign(cross(p1,p2,p3))
const int MXN = 1e5 + 6;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n, m;
struct P {
db x, y;
P(){};
P(db _x, db _y):x(_x),y(_y){}
P operator+(P p){return {x+p.x,y+p.y};}
P operator-(P p){return {x-p.x,y-p.y};}
db operator *(P s) {return x * s.x + y * s.y;}
db operator ^(P s) {return x * s.y - y * s.x;}
db dot(P p) {return x*p.x+y*p.y;}
db det(P p) {return x*p.y-y*p.x;}
};
inline int sign(db a) {return a < -eps?-1:a>eps;}
inline int cmp(db a, db b) {return sign(a-b);}
db dis_pp(P a, P b) {return sqrt((b - a)*(b - a));}//两点距离
db dot_p(P a, P b, P c) {return (b - a) * (c - a);}//点乘
double disToLine(P c,P A,P B){return fabs(cross(A,B,c))/dis_pp(A,B);}
//
int judge(P a, P b, P c) { //判断c是否在ab线段上(前提是c在直线ab上)
if(c.x >= min(a.x, b.x) && c.x <= max(a.x, b.x)
&& c.y >= min(a.y, b.y) && c.y <= max(a.y, b.y))return 1;
return 0;
}
db area(P b, P c, db r) {
P a(0.0, 0.0);
if (dis_pp(b, c) < eps) return 0.0;
db h = fabs(cross(a, b, c)) / dis_pp(b, c);
if(dis_pp(a, b) > r - eps && dis_pp(a, c) > r - eps) { //两个端点都在圆的外面则分为两种情况
db angle = acos(dot_p(a, b, c) / dis_pp(a, b) / dis_pp(a, c));
if(h > r - eps) {
return 0.5 * r * r * angle;
} else if(dot_p(b, a, c) > 0 && dot_p(c, a, b) > 0) {
db angle1 = 2 * acos(h / r);
return 0.5 * r * r * fabs(angle - angle1) + 0.5 * r * r * sin(angle1);
} else {
return 0.5 * r * r * angle;
}
} else if(dis_pp(a, b) < r + eps && dis_pp(a, c) < r + eps) { //两个端点都在圆内的情况
return 0.5 * fabs(cross(a, b, c));
} else { //一个端点在圆上一个端点在圆内的情况
if(dis_pp(a, b) > dis_pp(a, c)) { //默认b在圆内
swap(b, c);
}
if(fabs(dis_pp(a, b)) < eps) { //ab距离为0直接返回0
return 0.0;
}
if(dot_p(b, a, c) < eps) {
db angle1 = acos(h / dis_pp(a, b));
db angle2 = acos(h / r) - angle1;
db angle3 = acos(h / dis_pp(a, c)) - acos(h / r);
return 0.5 * dis_pp(a, b) * r * sin(angle2) + 0.5 * r * r * angle3;
} else {
db angle1 = acos(h / dis_pp(a, b));
db angle2 = acos(h / r);
db angle3 = acos(h / dis_pp(a, c)) - angle2;
return 0.5 * r * dis_pp(a, b) * sin(angle1 + angle2) + 0.5 * r * r * angle3;
}
}
}
P myp[MXN], ar[MXN];
db get_s(int n, db R, P O) { //求圆与多边形面积并
for (int i = 1; i <= n + 1; i++) myp[i] = ar[i];//顺或逆时针顺序
for(int i = 1; i <= n + 1; i++) myp[i] = myp[i] - O;
O = P(0, 0);
db sum = 0;
for(int i = 1; i <= n; i++) {
int j = i + 1;
db s = area(myp[i], myp[j], R);
if(cross(O, myp[i], myp[j]) > 0) sum += s;
else sum -= s;
}
return fabs(sum);
}
//
bool cmp1(P a, P b) { //3 4 1 2
double p = atan2(a.y, a.x), q = atan2(b.y, b.x);
if(p != q) return p < q;
return a.x < b.x;
}
bool cmp2(P a, P b) { //2 3 4 1 2
P c(0, 0);//尽量别单独使用
double tmp = cross(a, b, c);
if(tmp == 0) return a.x < b.x;
return tmp > 0;
}
int Quadrant1(P a) { //象限排序,注意x负半轴
if(a.x>0&&a.y>=0) return 3;
if(a.x<=0&&a.y>0) return 4;
if(a.x<0&&a.y<=0) return 1;
if(a.x>=0&&a.y<0) return 2;
return -1;
}
bool cmp5(P &a, P &b) {
int qa = Quadrant1(a), qb = Quadrant1(b);
if(qa == qb) return cmp2(a, b);
return qa < qb;
}
bool intersect(db l1, db r1, db l2, db r2) {
if(l1 > r1) swap(l1, r1);if(l2 > r2) swap(l2, r2);
return !(cmp(r1,l2)==-1||cmp(r2,l1)==-1);
}
bool isSS(P p1, P p2, P q1, P q2) {//判断线段相交
return intersect(p1.x,p2.x,q1.x,q2.x)&&intersect(p1.y,p2.y,q1.y,q2.y)&&
crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<=0&&crossOp(q1,q2,p1)*crossOp(q1,q2,p2)<=0;
}
bool isSS_strict(P p1, P p2, P q1, P q2) {//严格相交
return crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<0&&crossOp(q1,q2,p1)
*crossOp(q1,q2,p2) < 0;
}
bool isMiddle(db a, db m, db b) {
return sign(a-m)==0||sign(b-m)==0||(a<m!=b<m);
}
bool isMiddle(P a, P m, P b) {
return isMiddle(a.x,m.x,b.x)&&isMiddle(a.y,m.y,b.y);
}
bool onSeg(P p1, P p2, P q) {
return crossOp(p1,p2,q) == 0 && isMiddle(p1,q,p2);
}
bool ojbk(P p1, P p2, P q1, P q2) {
P p = p2 - p1;
P q = q2 - q1;
if(q.det(p) == 0) return 1;
return 0;
}
double rad(P p1, P p2) {
return atan2(p1.det(p2),p1.dot(p2));
}
bool xielv(P p1, P p2, P q1, P q2) {
P p = p2 - p1;
P q = q2 - q1;
if(q.det(p) == 0 && cmp(rad(p, q), 0) == 0) return 1;
return 0;
}
double _R, ans;
P cur(0, 0);
mt19937 lh(std::clock());
double Rand() {return 1.0*(lh()%INF)/INF;}
void simulateAnneal() {
ans = get_s(n, _R, cur);
double step = 30;
for(int iter = 0; iter < 2048; ++ iter) {
P best = cur;
db bestArea = ans;
bool improve = 0;
db dir = Rand();
const int K = 50;
db stepAngle = PI * 2/ K;
for(int i = 0; i < K; ++i) {
P nex{best.x+cos(dir)*step, best.y+sin(dir)*step};
db area = get_s(n, _R, nex);
if(area > bestArea) {
bestArea = area;
best = nex;
improve = 1;
}
dir += stepAngle;
}
if(improve) {
ans = max(ans, bestArea);
cur = best;
step *= 1.2;
}else {
step *= 0.9;
}
}
}
int main(){
scanf("%d%lf", &n, &_R);
double L = 1000, R = -1000;
for(int i = 1; i <= n; i++) {
scanf("%lf%lf", &ar[i].x, &ar[i].y);
L = min(L, ar[i].x); R = max(R, ar[i].x);
cur = cur + ar[i];
}
cur.x /= n, cur.y /= n;;
reverse(ar+1, ar+1+n);
ar[n + 1] = ar[1];
simulateAnneal();
printf("%.8f\n", ans);
return 0;
}
三分套三分
先三分 $x $ ,再三分 $y $ 。
第一个三分的上下限是 最小横坐标 和 最大横坐标。
第二个三分的上下限是 由三分\(x\)出的这条直线 与 凸多边形交点的 最小纵坐标 和 最大纵坐标。
#include<bits/stdc++.h>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
#define pb push_back
#define o2(x) (x)*(x)
#define db double
using namespace std;
typedef long long LL;
typedef pair<int, LL> pii;
#define cross(p1,p2,p3) ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y))
#define crossOp(p1,p2,p3) sign(cross(p1,p2,p3))
const int MXN = 1e5 + 6;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n, m;
struct P {
db x, y;
P(){};
P(db _x, db _y):x(_x),y(_y){}
P operator+(P p){return {x+p.x,y+p.y};}
P operator-(P p){return {x-p.x,y-p.y};}
db operator *(P s) {return x * s.x + y * s.y;}
db operator ^(P s) {return x * s.y - y * s.x;}
db dot(P p) {return x*p.x+y*p.y;}
db det(P p) {return x*p.y-y*p.x;}
};
inline int sign(db a) {return a < -eps?-1:a>eps;}
inline int cmp(db a, db b) {return sign(a-b);}
db dis_pp(P a, P b) {return sqrt((b - a)*(b - a));}//两点距离
db dot_p(P a, P b, P c) {return (b - a) * (c - a);}//点乘
double disToLine(P c,P A,P B){return fabs(cross(A,B,c))/dis_pp(A,B);}
//
int judge(P a, P b, P c) { //判断c是否在ab线段上(前提是c在直线ab上)
if(c.x >= min(a.x, b.x) && c.x <= max(a.x, b.x)
&& c.y >= min(a.y, b.y) && c.y <= max(a.y, b.y))return 1;
return 0;
}
db area(P b, P c, db r) {
P a(0.0, 0.0);
if (dis_pp(b, c) < eps) return 0.0;
db h = fabs(cross(a, b, c)) / dis_pp(b, c);
if(dis_pp(a, b) > r - eps && dis_pp(a, c) > r - eps) { //两个端点都在圆的外面则分为两种情况
db angle = acos(dot_p(a, b, c) / dis_pp(a, b) / dis_pp(a, c));
if(h > r - eps) {
return 0.5 * r * r * angle;
} else if(dot_p(b, a, c) > 0 && dot_p(c, a, b) > 0) {
db angle1 = 2 * acos(h / r);
return 0.5 * r * r * fabs(angle - angle1) + 0.5 * r * r * sin(angle1);
} else {
return 0.5 * r * r * angle;
}
} else if(dis_pp(a, b) < r + eps && dis_pp(a, c) < r + eps) { //两个端点都在圆内的情况
return 0.5 * fabs(cross(a, b, c));
} else { //一个端点在圆上一个端点在圆内的情况
if(dis_pp(a, b) > dis_pp(a, c)) { //默认b在圆内
swap(b, c);
}
if(fabs(dis_pp(a, b)) < eps) { //ab距离为0直接返回0
return 0.0;
}
if(dot_p(b, a, c) < eps) {
db angle1 = acos(h / dis_pp(a, b));
db angle2 = acos(h / r) - angle1;
db angle3 = acos(h / dis_pp(a, c)) - acos(h / r);
return 0.5 * dis_pp(a, b) * r * sin(angle2) + 0.5 * r * r * angle3;
} else {
db angle1 = acos(h / dis_pp(a, b));
db angle2 = acos(h / r);
db angle3 = acos(h / dis_pp(a, c)) - angle2;
return 0.5 * r * dis_pp(a, b) * sin(angle1 + angle2) + 0.5 * r * r * angle3;
}
}
}
P myp[MXN], ar[MXN];
db get_s(int n, db R, P O) { //求圆与多边形面积并
for (int i = 1; i <= n + 1; i++) myp[i] = ar[i];//顺或逆时针顺序
for(int i = 1; i <= n + 1; i++) myp[i] = myp[i] - O;
O = P(0, 0);
db sum = 0;
for(int i = 1; i <= n; i++) {
int j = i + 1;
db s = area(myp[i], myp[j], R);
if(cross(O, myp[i], myp[j]) > 0) sum += s;
else sum -= s;
}
return fabs(sum);
}
//
bool cmp1(P a, P b) { //3 4 1 2
double p = atan2(a.y, a.x), q = atan2(b.y, b.x);
if(p != q) return p < q;
return a.x < b.x;
}
bool cmp2(P a, P b) { //2 3 4 1 2
P c(0, 0);//尽量别单独使用
double tmp = cross(a, b, c);
if(tmp == 0) return a.x < b.x;
return tmp > 0;
}
int Quadrant1(P a) { //象限排序,注意x负半轴
if(a.x>0&&a.y>=0) return 3;
if(a.x<=0&&a.y>0) return 4;
if(a.x<0&&a.y<=0) return 1;
if(a.x>=0&&a.y<0) return 2;
return -1;
}
bool cmp5(P &a, P &b) {
int qa = Quadrant1(a), qb = Quadrant1(b);
if(qa == qb) return cmp2(a, b);
return qa < qb;
}
bool intersect(db l1, db r1, db l2, db r2) {
if(l1 > r1) swap(l1, r1);if(l2 > r2) swap(l2, r2);
return !(cmp(r1,l2)==-1||cmp(r2,l1)==-1);
}
bool isSS(P p1, P p2, P q1, P q2) {//判断线段相交
return intersect(p1.x,p2.x,q1.x,q2.x)&&intersect(p1.y,p2.y,q1.y,q2.y)&&
crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<=0&&crossOp(q1,q2,p1)*crossOp(q1,q2,p2)<=0;
}
bool isSS_strict(P p1, P p2, P q1, P q2) {//严格相交
return crossOp(p1,p2,q1)*crossOp(p1,p2,q2)<0&&crossOp(q1,q2,p1)
*crossOp(q1,q2,p2) < 0;
}
bool isMiddle(db a, db m, db b) {
return sign(a-m)==0||sign(b-m)==0||(a<m!=b<m);
}
bool isMiddle(P a, P m, P b) {
return isMiddle(a.x,m.x,b.x)&&isMiddle(a.y,m.y,b.y);
}
bool onSeg(P p1, P p2, P q) {
return crossOp(p1,p2,q) == 0 && isMiddle(p1,q,p2);
}
bool ojbk(P p1, P p2, P q1, P q2) {
P p = p2 - p1;
P q = q2 - q1;
if(q.det(p) == 0) return 1;
return 0;
}
double rad(P p1, P p2) {
return atan2(p1.det(p2),p1.dot(p2));
}
bool xielv(P p1, P p2, P q1, P q2) {
P p = p2 - p1;
P q = q2 - q1;
if(q.det(p) == 0 && cmp(rad(p, q), 0) == 0) return 1;
return 0;
}
double _R;
double check1(double x) {
double L = 10000, R = -10000, midl, midr, tmpl, tmpr, ans = 0;
for(int i = 1; i <= n; ++i) {
if(sign((ar[i].x-x)*(ar[i+1].x-x)) <= 0) {
double tmp = ar[i].y + (ar[i+1].y-ar[i].y)/(ar[i+1].x-ar[i].x)*(x-ar[i].x);
L = min(L, tmp);
R = max(R, tmp);
}
}
for(int i = 0; i < 80; ++i) {
midl = (L + L + R) / 3;
midr = (L + R + R) / 3;
tmpl = get_s(n, _R, P{x, midl}); tmpr = get_s(n, _R, P{x, midr});
if(tmpl > tmpr) {
ans = max(ans, tmpl);
R = midr;
}else {
ans = max(ans, tmpr);
L = midl;
}
}
return ans;
}
int main(){
scanf("%d%lf", &n, &_R);
double L = 1000, R = -1000;
for(int i = 1; i <= n; i++) {
scanf("%lf%lf", &ar[i].x, &ar[i].y);
L = min(L, ar[i].x); R = max(R, ar[i].x);
}
reverse(ar+1, ar+1+n);
ar[n + 1] = ar[1];
double midl, midr, tmpl, tmpr, ans = 0;
for(int i = 0; i < 80; ++i) {
midl = (L + L + R) / 3;
midr = (L + R + R) / 3;
tmpl = check1(midl); tmpr = check1(midr);
if(tmpl > tmpr) {
ans = max(ans, tmpl);
R = midr;
}else {
ans = max(ans, tmpr);
L = midl;
}
}
printf("%.8f\n", ans);
return 0;
}
模拟退火套路
for(int tc = 0; tc < 100; tc++) {//模拟退火次数
pdd o;//随机起点
double d = 400;//初始温度
double area;//最优解
while (d > eps) {//终止温度
pdd nex = o + rand();//随机移动
double tot;//当前答案
if (tot > area) o = nex, area = tot;//更新最优解
d *= 0.99;//降温系数0.998,0.975
}
ans = max(ans, area);//更新答案
}
double _R, ans;
P cur(0, 0);
mt19937 lh(std::clock());
double Rand() {return 1.0*(lh()%INF)/INF;}
void simulateAnneal() {
ans = get_s(n, _R, cur);
double step = 30;//看题目范围咯
for(int iter = 0; iter < 2048; ++ iter) {//或者改成>eps
P best = cur;
db bestArea = ans;
bool improve = 0;
db dir = Rand();
const int K = 50;//试探50个方向
db stepAngle = PI * 2 / K;
for(int i = 0; i < K; ++i) {
P nex{best.x+cos(dir)*step, best.y+sin(dir)*step};
db area = get_s(n, _R, nex);
if(area > bestArea) {
bestArea = area;
best = nex;
improve = 1;
}
dir += stepAngle;
}
if(improve) {
ans = max(ans, bestArea);
cur = best;
step *= 1.2;
}else {
step *= 0.9;
}
}
}
Gym101158 J 三分 or 模拟退火 Cover the Polygon with Your Disk的更多相关文章
- Gym-101158J Cover the Polygon with Your Disk 计算几何 求动圆与多边形最大面积交
题面 题意:给出小于10个点形成的凸多边形 和一个半径为r 可以移动的圆 求圆心在何处的面积交最大,面积为多少 题解:三分套三分求出圆心位置,再用圆与多边形面积求交 #include<bits/ ...
- Soj题目分类
-----------------------------最优化问题------------------------------------- ----------------------常规动态规划 ...
- HDU 3642 Get The Treasury (线段树扫描线)
题意:给你一些长方体,问你覆盖三次及以上的体积有多大 首先我们观察x轴y轴一样很大,但是z轴很小,所以我们可以枚举z轴(-500,500),注意我们枚举的是每一段长度为一的z轴的xy轴的面积而不是点. ...
- 百度地图API功能集锦
1.点个数太多导致加载缓慢的解决. 2.可视化区域内加载的解决. 3.自定义信息窗口解决. 4.区域/板块/商圈等的绘制功能解决. 基本包含了用到百度地图API会使用到的大部分常规性场景.(聚合点功能 ...
- JQuery lhgdialog使用
jQuery方式调用 J ); testDG4.SetPosition( 'center', 'center' );}; var testDG4 = J('#btn26').dialog({ id:' ...
- 【BZOJ-2618】凸多边形 计算几何 + 半平面交 + 增量法 + 三角剖分
2618: [Cqoi2006]凸多边形 Time Limit: 5 Sec Memory Limit: 128 MBSubmit: 959 Solved: 489[Submit][Status] ...
- HDU 3957 Street Fighter(搜索、DLX、重复覆盖+精确覆盖)
很久以前就看到的一个经典题,一直没做,今天拿来练手.街霸 给n<=25个角色,每个角色有 1 or 2 个版本(可以理解为普通版以及爆发版),每个角色版本可以KO掉若干人. 问最少选多少个角色( ...
- javascript: jquery.gomap-1.3.3.js
from:http://www.pittss.lv/jquery/gomap/solutions.php jquery.gomap-1.3.3.js: /** * jQuery goMap * * @ ...
- 最小圆覆盖(Smallest Enclosing Discs)
随机增量算法(a randomized incremental algorithm) #define sqr(x) ((x)*(x)) #define EPS 1e-4 struct P{ doubl ...
随机推荐
- RabbitMQ的简单应用【转】
虽然后台使用了读写分离技术,能够在一定程度上抗击高并发,但是如果并发量特别巨大时,主数据库不能同时处理高并发的请求,这时数据库容易宕机. 问题: 现在的问题是如何既能保证数据库正常运行,又能实现用户数 ...
- JS-for..of
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...of 刚刚上网上看到<V8 ...
- centos7运行yum报如下提示:Run "yum repolist all" to see the repos you have
centos7运行yum报如下提示: There are no enabled repos. Run "yum repolist all" to see the repos you ...
- 抓包工具fiddler下载配置(三):手机设置代理
前言 本篇仅讲解了手机端如何设置代理,是[抓包工具fiddler下载配置(一):下载/安装&信任证书]的后续文章,未下载安装抓包工具的需先参考文章[抓包工具fiddler下载配置(一):下 ...
- 网络命令-nc(一)
一直在linux环境下编程,但却没有用过nc命令,不过最近发现Netcat这个命令-nc,发现真的蛮强大的, 为了备忘,就写了这个博客吧,不求全,只求把自己觉得很有用的命令整理出来,这篇文章估计要长期 ...
- WireShark 自带工具 editcap 和 text2pcap 配合完成改包操作
一.拆包 首先声明这种方法比较复杂而且需要点技术水平,不建议菜鸟尝试(可以使用WireEdit编辑pcap包,不过要联网)其实在熟练这种方法后也可以很快的,但这种方法主要还是方便吧,不用下载其他什么软 ...
- PHP递归获得树形菜单和遍历文件夹下的所有文件以及子文件夹
PHP递归获得树形菜单和遍历文件夹下的所有文件以及子文件夹 一.使用递归获取树形菜单 数据表category(id,name,parent_id) <?php class category{ / ...
- jquery 的几种写法和常见问题
为了理解页面初始化事件的编写和执行方式,特此记录下页面加载事件的语句方式: //最简单的加载事件语句 $(function(){ alert("这个提示框最先弹出")//这个用的最 ...
- ssh 免密码登录实现批量处理
搭建集群的时候ssh 免密码登录是一个问题以下脚本将实现批量处理 文件1主机名:host 17.19.18.11:12317.19.18.12:123 文件2:ssh_setup.py #!/usr/ ...
- C++中逗号操作符重载的分析
1,关注逗号操作符重载后带来的变化: 2,逗号操作符: 1,逗号操作符(,)可以构成都好表达式:exp1, exp2, exp3, ..., expN 1,逗号表达式用于将多个表达式连接为一个表达式: ...