「Gym101158J」Cover the Polygon with Your Disk【模擬退火】

Cover the Polygon with Your Disk

Input: Standard Input
Time Limit: 5 seconds

A convex polygon is drawn on a flat paper sheet. You are trying to place a disk in your hands to cover as large area of the polygon as possible. In other words, the intersection area of the polygon and the disk should be maximized.

Input

The input consists of a single test case, formatted as follows. All input items are integers.
n rn\ r
x1 y1x_1\ y_1
.
.
.
xn ynx_n\ y_n
nn is the number of vertices of the polygon (3n10).(3 \leq n \leq 10). r is the radius of the disk (1r100).(1 \leq r \leq 100). xix_i and yiy_i give the coordinate values of the ithi-th vertex of the polygon (1in).(1 \leq i \leq n). Coordinate values satisfy 0xi1000 \leq x_i \leq 100 and 0yi100.0 \leq y_i \leq 100.The vertices are given in counterclockwise order. As stated above, the given polygon is convex.In other words, interior angles at all of its vertices are less than 180180. Note that the border of a convex polygon never crosses or touches itself.

Output

Output the largest possible intersection area of the polygon and the disk. The answer should not have an error greater than 0.0001(104).0.0001 (10^{−4}).

Sample Input 1

4 4
0 0
6 0
6 6
0 6

Sample Output 1

35.759506

Sample Input 2

3 1
0 0
2 1
1 3

Sample Output 2

2.113100

Sample Input 3

3 1
0 0
100 1
99 1

Sample Output 3

0.019798

Sample Input 4

4 1
0 0
100 10
100 12
0 1

Sample Output 4

3.137569

Sample Input 5

10 10
0 0
10 0
20 1
30 3
40 6
50 10
60 15
70 21
80 28
90 36

Sample Output 5

177.728187

Sample Input 6

10 49
50 0
79 10
96 32
96 68
79 90
50 100
21 90
4 68
4 32
21 10

Sample Output 6

7181.603297

題意

  • 就是給你一個凸多邊形,以及某一個圓的半徑rr,然後讓你去找一個平面上的圓心,使得這個半徑爲rr的圓與凸多邊形相交的面積最大

題解

  • 因爲是凸多邊形,不難想到三分套三分去搞
  • 然而可以模擬退火去做,別問我爲啥,因爲模擬退火就是找一個最優的實數點的
  • 但是這個不用模擬退火中的那個“不是比局部最優解更優的答案對應的隨機出來的點以一定概率去接受這個點”,用了答案不對?希望路過的大佬賜教,去掉就對了,感覺這不是模擬退火而變成了爬山算法?

代碼

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
const int maxn=20005;
#define inf 0x3f3f3f3f3f3f3f3f
#define pi acos(-1.0)
 
int sgn(double k) {return k<-eps?-1:(k<eps?0:1);}
double Acos(double a) {return a<=-1?pi:(a>=1?0:acos(a));}
double Sqrt(double a) {return sgn(a)==0?0:sqrt(a);}
 
typedef struct point{  //點結構體
    double x,y;
    point(double a=0,double b=0) {x=a;y=b;} 
    point operator+(point other) {return point(x+other.x,y+other.y);}
    point operator-(point other) {return point(x-other.x,y-other.y);}
    point operator*(double k) {return point(x*k,y*k);}
    point operator/(double k) {return point(x/k,y/k);} 
    //點乘,即數量積,內積(ABcos<A,B>)
    double operator*(point other) {return x*other.x+y*other.y;}
    //叉乘,即向量積,外積(ABsin<A,B>)
    double operator^(point other) {return x*other.y-y*other.x;}
    friend bool operator<(const point &p1,const point &p2) {
        if(sgn(p1.x-p2.x)==0) return p1.y<p2.y;
        return p1.x<p2.x;
    }
    static bool compare_x(const point &p1,const point &p2) {return p1.x<p2.x;}
    static bool compare_y(const point &p1,const point &p2) {return p1.y<p2.y;}
    friend bool operator==(const point &p1,const point &p2) {
        return sgn(p1.x-p2.x)==0&&sgn(p1.y-p2.y)==0;
    }
    friend bool operator!=(const point &p1,const point &p2) {
        return sgn(p1.x-p2.x)!=0||sgn(p1.y-p2.y)!=0;
    }
    //o-p長度
    friend double len(point p) {return Sqrt(p.x*p.x+p.y*p.y);}
    friend double len2(point p) {return p.x*p.x+p.y*p.y;}
    //op與x軸夾角,範圍[-pi,pi]
    friend double angle(point p) {return atan2(p.y,p.x);}    
    //判斷點和有向直線的關係: -1:直線s-e方向左側 0:直線上 1:直線右側
    friend int point_to_line(point p,point s,point e) {return -sgn((e-s)^(p-s));}
    //op1-op2無方向角度,即與原點連線夾腳,返回值的範圍[0,pi]
    friend double angle(point p1,point p2) {return Acos((p1*p2)/len(p1)/len(p2));}
    //將向量p長度變爲len返回一個新的向量,方向不變
    friend point extend(point p,double l) { 
        if(sgn(len(p))==0) return point(0,l);  //0向量
        return point(p*(l/len(p)));
    }
    //點p2繞着點p1順時針方向旋轉a角度,a爲弧度,返回新的point
    friend point rotate(point p1,point p2,double a) {
        point vec=p2-p1;
        double xx=vec.x*cos(a)+vec.y*sin(a);
        double yy=vec.y*cos(a)-vec.x*sin(a);
        return point(p1.x+xx,p1.y+yy);
    }
    //三角形面積
    friend double area(point p1,point p2,point p3) {
        return fabs(((p2-p1)^(p3-p1))/2);
    }
}Vector;
 
 
struct line{
    point s,e;
    line(point a,point b) {s=a;e=b;}
    line(double a=0,double b=0,double c=0,double d=0) {s=point(a,b);e=point(c,d);}
    //半平面交所用極角排序函數
    static bool compare_half_plane(line a,line b){
        double A=angle(a.e-a.s),B=angle(b.e-b.s);
        if(sgn(A-B)!=0) return A<B;
        return sgn((a.e-a.s)^(b.e-a.s))>0;
    }
    //計算直線p1-p2是否與直線q1-q2的交點,注意不能平行
    /*需要重載point*double  */
    friend point intersect(line l1,line l2) {
        double k=((l2.e-l2.s)^(l2.s-l1.s))/((l2.e-l2.s)^(l1.e-l1.s));
        return l1.s+(l1.e-l1.s)*k;
    }
    //返回直線傾斜角  0<=angle<=pi
    friend double angle(line p) {
        double k=atan2(p.e.y-p.s.y,p.e.x-p.s.x);
        if(sgn(k)==0) k+=pi;
        if(sgn(k-pi)==0) k-=pi;
        return k;
    }
 
    //兩條直線平行或重合
    friend  bool parallel(line l1,line l2) {
        return sgn((l1.e-l1.s)^(l2.e-l2.s))==0;
    }
 
    //判斷點是否在線段上
    friend bool onseg(point p,line l) {
        return sgn((l.s-p)^(l.e-p))==0&&sgn((l.s-p)*(l.e-p))<=0;
    } 
    //判斷點和有向直線的關係: -1:直線s-e方向左側 0:直線上 1:直線右側
    friend int point_to_line(point p,line l) {
        return -sgn((l.e-l.s)^(p-l.s));
    }
    friend bool onright(point p,line l) {
        return ((l.e-l.s)^(p-l.s))<=0;
    }
    //線段與線段關係: 0:平行沒有交點 1:平行有交點 2:不平行沒有交點 3:不平行有交點
    friend int seg_to_seg(line l1,line l2) {
        if(sgn((l1.e-l1.s)^(l2.e-l2.s))==0) {
            if(onseg(l1.s,l2)||onseg(l1.e,l2)||onseg(l2.s,l1)||onseg(l2.e,l1)) return 1;
            else return 0;
        }
        point inter=intersect(l1,l2);
        if(onseg(inter,l1)&&onseg(inter,l2)) return 3;
        return 2;
    }
 
    //兩直線關係: 0:平行 1:重合 2:相交
    friend int line_to_line(line l1,line l2) {
        if(parallel(l1,l2)) return point_to_line(l1.s,l2)==0;
        return 2;
    }
    //點a到直線l的距離
    friend double dis_point_line(point p,line l) { 
        if(onseg(p,l)) return 0;
        double cos0=(p-l.s)*(l.e-l.s)/(len(p-l.s)*len(l.e-l.s));
        return len(p-l.s)*sin(Acos(cos0));
    }
    //點到線段l的距離
    friend double dis_point_seg(point p,line l) {
        if(sgn((p-l.s)*(l.e-l.s))<0||sgn((p-l.e)*(l.s-l.e))<0) return min(len(p-l.s),len(p-l.e));
        return dis_point_line(p,l);
    }
    //線段到線段距離
    friend double dis_seg_seg(line l1,line l2) {
        if(seg_to_seg(l1,l2)==1||seg_to_seg(l1,l2)==3) return 0;
        return min(dis_point_seg(l1.s,l2),min(dis_point_seg(l1.e,l2),min(dis_point_seg(l2.s,l1),dis_point_seg(l2.e,l1))));
    }
 
    //返回點a在直線l上的垂足
    friend point chuizu(point p,line l) {
        return l.s+((l.e-l.s)*((l.e-l.s)*(p-l.s)))/len2(l.e-l.s);
    } 
    //點a關於l的對稱點
    friend point duichen(point p,line l) {
        point q=chuizu(p,l);
        return point(2*q.x-p.x,2*q.y-p.y);
    }
 
};
 
struct circle{
    point o;double r;
    circle(point a,double b) {o=a;r=b;}
    circle(double a=0,double b=0,double c=0) {o.x=a,o.y=b,r=c;}
    bool operator==(circle c) {return o==c.o&&sgn(r-c.r)==0;}
    double area() {return pi*r*r;}
 
    //求三角形外接圓
    circle(point a,point b,point c) {
        point p1=rotate((a+b)/2,b,3*pi/2),p2=rotate((b+c)/2,c,3*pi/2);
        o=intersect(line((a+b)/2,p1),line((b+c)/2,p2));
        r=len(o-a);
    }
 
    //求三角形內切圓,nouse無實際作用,只是用來區別上面外接圓
    circle(point a,point b,point c,int nouse){
        if(sgn((b-a)^(c-b))<0) swap(b,c);
        point p1=rotate(a,b,2*pi-(angle(c-a,b-a)/2)),p2=rotate(b,c,2*pi-(angle(a-b,c-b)/2));
        o=intersect(line(a,p1),line(b,p2));
        r=dis_point_line(o,line(b,c));
    }
 
    //點和圓的關係:0:圓內 1:圓上 2:圓外
    friend int point_to_circle(point p,circle c) {
        return 1+sgn(len(p-c.o)-c.r);
    }
    //直線和圓的關係:0:與圓相交 1:圓上 2:圓外
    friend int line_to_circle(line l,circle c) {
        return 1+sgn(dis_point_line(c.o,l)-c.r);
    }
 
    //返回直線與圓交點個數,交點座標存在引用p1,p2中
    friend int line_circle_cross(line l,circle c,point &p1,point &p2) {
        if(line_to_circle(l,c)==2) return 0;
        double d=dis_point_line(c.o,l);
        point p=chuizu(c.o,l);
        d=Sqrt(c.r*c.r-d*d);
        if(sgn(d)==0) {
            p1=p2=p;
            return 1;
        }
        p1=p+extend(l.e-l.s,d);p2=p+extend(l.e-l.s,-d);
        return 2;
    }
 
    //返回三角形oab與圓相交的面積
    friend double circle_triangle_area(circle c,point a,point b) {
        if(sgn((a-c.o)^(b-c.o))==0) return 0.0;
        point q[5],p1,p2;int len=0;q[len++]=a;line l=line(a,b);
        if(line_circle_cross(l,c,q[1],q[2])==2) {
            if(sgn((a-q[1])*(b-q[1]))<0) q[len++]=q[1];
            if(sgn((a-q[2])*(b-q[2]))<0) q[len++]=q[2];
        }
        q[len++]=b;double res=0;
        if(len==4&&sgn((q[0]-q[1])*(q[2]-q[1]))>0) swap(q[1],q[2]);
        for(int i=0;i<len-1;i++) {
            if(point_to_circle(q[i],c)==2||point_to_circle(q[i+1],c)==2) {
                double ang=angle(q[i]-c.o,q[i+1]-c.o);
                res+=c.r*c.r*ang/2;
            }else res+=fabs((q[i]-c.o)^(q[i+1]-c.o))/2.0;
        }
        return res;
    }
 
};
 
struct polygon{
    int n;
    point p[maxn];
    line l[maxn];
 
    //求簡單多邊形與圓相交的面積
    friend double polygon_circle_area(polygon &pol,circle c) {
        double ans=0;
        for(int i=1;i<=pol.n;i++) {
            int j=i%pol.n+1;
            if(sgn((pol.p[j]-c.o)^(pol.p[i]-c.o))>=0) ans+=circle_triangle_area(c,pol.p[i],pol.p[j]);
            else ans-=circle_triangle_area(c,pol.p[i],pol.p[j]);
        }
        return fabs(ans);
    }
 
    void input() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
    }
    void output() {
        printf("n=%d\n",n);
        for(int i=1;i<=n;i++) printf("%lf %lf\n",p[i].x,p[i].y);
    }
};
 
polygon pol;
double r;
int n;
namespace simulated_annealing {
    double ansx,ansy; //全局最優解的座標
    double ans;       // 全局最優解,溫度
    const double delta=0.996; //降溫係數
    inline double dis(double a,double b,double c,double d) {
        return sqrt((a-c)*(a-c)+(b-d)*(b-d));
    }
    inline double calc_energy(double a,double b) {  //計算決策點在(a,b)處時的能量
        return polygon_circle_area(pol,circle(a,b,r));
    }   
    inline void init() {     //初始化
        ansx=ansy=0;
        for(int i=1;i<=n;i++) ansx+=pol.p[i].x,ansy+=pol.p[i].y;
        ansx/=n,ansy/=n;
        ans=calc_energy(ansx,ansy);
    }
    inline void simulate() {
        double nowx=ansx,nowy=ansy; //當前搜索到的點
        double t=100000;
        while(t>1e-14) {
            double nxtx=nowx+(rand()*2-RAND_MAX)*t;
            double nxty=nowy+(rand()*2-RAND_MAX)*t;
            double nxt_energy=calc_energy(nxtx,nxty);
            double delta_energy=nxt_energy-ans;        //能量差
            if(delta_energy>0) nowx=ansx=nxtx,nowy=ansy=nxty,ans=nxt_energy;      //能量降低一定接受新點
            else if(exp(-delta_energy/t)*RAND_MAX>rand()) nowx=nxtx,nowy=nxty;   //以一定的概率接受這個點
            t*=delta;       //降溫
        }
    }
    inline void solve() {
        init();
        simulate();
        simulate();
        simulate();
        simulate();
        simulate();
        simulate();
        simulate();
    }
}
using namespace simulated_annealing;
 
int main()
{
    scanf("%d %lf",&pol.n,&r);n=pol.n;
    for(int i=1;i<=pol.n;i++) scanf("%lf %lf",&pol.p[i].x,&pol.p[i].y);
    solve();
    printf("%.10lf\n",ans);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章