A Chocolate Manufacturer's Problem HDU - 3644 (模拟退火法)

My mama always said:Life is like a box of chocolate, you never know what you are going to get. 
——From Forrest Gump



ACM is a chocolate manufacturer. Inspired by the famous quote above, recently, they are designing a new brand of chocolate named Life. Each piece of chocolate is a random simple polygon. After molding and shaping, every piece is put in a small box. Until you open the box, you will not know what you will get: a huge piece or only a tiny fraction. It is really like life and that is the reason it is named for. 

However, here comes a problem. The manufacturer has to print the logo on each piece of chocolate. The logo is a circle with ‘ACM’ inside. Here is an example below. It is fortunate that the logo can be printed on the chocolate. 
 
Now the manufacturer is asking you for help. Given the information about the chocolate shape and the radius of the logo, you need to judge whether or not there is enough space to print the logo. 

Input

The input contains no more than 20 cases, and each case is formatted as follows. 

x 1 y 1 
x 2 y 2 
… 
x n y n 

The first line is the number of vertices of the polygon, n, which satisfies 3 ≤ n ≤ 50. Following n lines are the x- and y-coordinates of the n vertices. They are float numbers and satisfy 0 ≤ x i ≤ 1000 and 0 ≤ y i ≤ 1000 (i = 1, …, n). Line segments (x i, y i)–(x i+ 1 , y i + 1) (i = 1, …, n) and the line segment (x n, y n)–(x1, y 1) form the border of the polygon. After the description of the polygon, a float number r follows, meaning the radius of the logo( r <= 1000). 
The input ends by a single zero. 
You may assume that the polygon is simple, that is, its border never crosses or touches itself. 

Output

For each case, output “Yes” if the logo is able to be printed on the chocolate, otherwise output “No” instead. 

Sample Input

3
0 0
0 1
1 0
0.28
3
0 0
0 1
1 0
0.3
0

Sample Output

Yes
No

思路:考虑在凸边行内部找圆心,从每条边的中点开始进行扩展,每次扩展的距离使用凸包的直径×随机数。

模拟退火法的思路是先以较大的步长向四周搜索,接着一步步缩小步长,继续搜索(这样做的目的是为了找到全局最优解),所以对模拟退火法的步长控制非常重要(一般设置为0.9可以稳,不过比较慢,可以考虑0.5以上,但能不能对就不知道了)。

代码及注释:

//hud 3644
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;
typedef long long ll;
const int mod=7340033;
const double eps=1e-8;
#define INF 0x3f3f3f3f
int times;
int n;
struct point
{
    double x, y, r;
} p[maxn], test[maxn];

inline int dblcmp( double x )
{
    if( fabs(x) < eps )
        return 0;
    return x > 0 ? 1 : -1;
}

inline double sqr( double x )
{
    return x*x;
}

double dis( point& aa, point& bb )
{
    return sqrt(sqr(aa.x-bb.x)+sqr(aa.y-bb.y));
}

double cross( point& k, point& aa, point& bb )
{
    return (k.x-aa.x)*(k.y-bb.y)-(k.y-aa.y)*(k.x-bb.x);
}

//线段到点的距离
double seg_point_dis( point& l1, point& l2, point& k )
{
    double a, b, c;
    a = dis(l1, k);
    b = dis(l2, k);
    c = dis(l1, l2);

    //这里的判断是由余弦定理推出来的
    if( dblcmp(a*a+c*c-b*b) < 0 )
        return a;
    else if( dblcmp(b*b+c*c-a*a) < 0 )
        return b;
    else
        return fabs(cross(k, l1, l2)/c);
}

//判断点是否在多边形内
//思想:过点p做一条射线,判断交点数
bool point_inside( point& aa )
{
    int i, cnt = 0;
    double t;

    //确保p[n] = p[0]
    for( i = 0; i < n; ++i )
    {
        if( (p[i].y <= aa.y && p[i+1].y > aa.y) ||
            (p[i+1].y <= aa.y && p[i].y > aa.y) )
        {
            if( !dblcmp(p[i].y-p[i+1].y) )
            {
                if( dblcmp(p[i].y-aa.y) == 0 )
                    cnt++;
                t = -INF;
            }
            else
                t = p[i+1].x - (p[i+1].x-p[i].x)*(p[i+1].y-aa.y)/(p[i+1].y-p[i].y);
            if( dblcmp( t - aa.x ) >= 0 )
                cnt++;
        }
    }
    return cnt%2;
}
void cal( point& aa )
{
    double t;
    aa.r = INF;
    for( int i = 0; i < n; ++i )
    {
        t = seg_point_dis(p[i], p[i+1], aa);
        aa.r = min(aa.r, t);
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    double R, delte, maxx, maxy, minx, miny, ang;
    point temp;
    bool ok;
    times=5;
    while(scanf("%d",&n)!=EOF&&n)
    {
        ok = 0;
        maxx = maxy = 0;
        minx = miny = INF;
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&p[i].x,&p[i].y);
            maxx = max(maxx, p[i].x);
            maxy = max(maxy, p[i].y);
            minx = min(minx, p[i].x);
            miny = min(miny, p[i].y);
        }
        p[n]=p[0];
        for(int i=0;i<n;i++)
        {
            test[i].x=(p[i].x+p[i+1].x)/2;//从线段中点开始
            test[i].y=(p[i].y+p[i+1].y)/2;
            test[i].r=0;
        }
        scanf("%lf",&R);
        maxx-=minx;
        maxy-=miny;
        delte=sqrt(maxx*maxx+maxy*maxy)/2;//设定最长偏移,因为是圆心,找一半就行
        while(ok==0&&(delte>eps))
        {
            for(int i=0;ok==0&&i<n;i++)
            {
                for(int j=0;ok==0&&j<times;j++)
                {
                    ang=rand();
                    temp.x = test[i].x + delte*cos(ang);
                    temp.y = test[i].y + delte*sin(ang);
                    if(point_inside(temp))//先判断在不在内部
                    {
                        cal(temp);//求出点到各边的最短距离
                        if( temp.r > test[i].r )
                        {
                            test[i] = temp;
                            if( dblcmp(temp.r-R) >= 0 )//满足题意
                                ok = 1;
                        }
                    }
                }
            }
            delte*=0.70;
        }
        if( ok )
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/82494812