HDU 3644 模拟退火的火候控制

#include <bits/stdc++.h>
#define LL long long
#define eps 1e-7
#define zero(a) fabs(a)<eps
const int INF = 0x3f3f3f3f;
const int N = 20;
const double pi = acos(-1.0);
int n;
using namespace std;
struct Point
{
    double x, y, val;
} p[100], t[100], pre, cur;
struct Segment
{
    Point a, b;
};
inline double xmul(Point p0, Point p1, Point p2)//差乘
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
inline double dist(Point p1, Point p2)
{
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
inline double Dist_Point_Seg(Point p, Point a, Point b)//点到直线距离
{
    Point t = p;
    t.x += a.y - b.y; t.y += b.x - a.x;
    if (xmul(a, t, p)*xmul(b, t, p) > eps)
        return dist(p, a) + eps < dist(p, b) ? dist(p, a) : dist(p, b);
    else
        return fabs(xmul(p, a, b)) / dist(a, b);
}
inline bool online(Point p1, Point p2, Point p)//点在线上
{
    if (zero(xmul(p1, p2, p)) && ((p.x - p1.x) * (p.x - p2.x) < eps && (p.y - p1.y) * (p.y - p2.y) < eps))
        return true;
    return false;
}
inline bool across(Segment s1, Segment s2)// 线段相交
{
    if (xmul(s1.a, s1.b, s2.a)*xmul(s1.a, s1.b, s2.b) < eps)
        if (xmul(s2.a, s2.b, s1.a)*xmul(s2.a, s2.b, s1.b) < eps)
            return true;
    return false;
}
inline bool Parallel(Segment s1, Segment s2)//平行
{
    return zero((s1.a.x - s1.b.x) * (s2.a.y - s2.b.y) - (s2.a.x - s2.b.x) * (s1.a.y - s1.b.y));
}
inline bool In_Polygon(Point cen)//判断在多边形内
{
    int cnt = 0;
    Segment s, e;
    s.a = cen; s.b.y = cen.y; s.b.x = 20000.0;
    for (int i = 0; i < n; i++)
    {
        e.a = p[i]; e.b = p[i + 1];
        if (online(p[i], p[i + 1], cen)) return false;
        if (zero(p[i].y - p[i + 1].y)) continue;
        if (online(s.a, s.b, p[i]))
        {
            if (p[i].y > p[i + 1].y) cnt++;
        }
        else if (online(s.a, s.b, p[i + 1]))
        {
            if (p[i + 1].y > p[i].y) cnt++;
        }
        else if (across(s, e))
            cnt++;
    }
    return cnt & 1;
}
inline void Get_Min_Dist(Point &cur)//点到多边形的最短距离
{
    double ret = INF;
    for (int i = 0; i < n; i++)
        ret = min(ret, Dist_Point_Seg(cur, p[i], p[i + 1]));
    cur.val = ret;
}
int main(int argc, char const *argv[])
{
    double r, best[105];
    srand(time(NULL));
    while (cin >> n && n)
    {
        double maxx = 0, maxy = 0, minx = INF, miny = INF;
        for (int i = 0; i < n; i++)
        {
            cin >> 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];
        cin >> r;
        int m = min(n, N);
        for (int i = 0; i < m; i++)//从线段中点开始运动
        {
            t[i].x = (p[i].x + p[i + 1].x) / 2.0;
            t[i].y = (p[i].y + p[i + 1].y) / 2.0;
            t[i].val = 0;
        }
        bool ok = 0;
        for (double step = sqrt((maxx - minx) * (maxx - minx) + (maxy - miny) * (maxy - miny)) / 2; step > 1e-3 && !ok; step *= 0.55)//模拟退火
            for (int i = 0; i < m && !ok; ++i)//对于每一个点都计算提高几率
                for (int j = 0; j < 5 && !ok; j++)//提高几率
                {
                    double angle = rand();
                    cur.x = t[i].x + step * cos(angle);
                    cur.y = t[i].y + step * sin(angle);
                    if (!In_Polygon(cur))continue;// 点跳出了多边形
                    Get_Min_Dist(cur);//在这个点最大的R
                    if (cur.val + 1e-3 > t[i].val)
                    {
                        t[i] = cur;
                        if (cur.val + 1e-3 > r) ok = 1;
                    }
                }
        cout << (ok ? "Yes" : "No") << endl;
    }
    return 0;
}


几何题,一个任意多边形,判断是否能放入一个半径为r的圆;

卡卡卡卡卡了好久,看了题解才知道还是too young

http://blog.csdn.net/ACM_cxlove/article/details/7898178

模拟退火关键问题在于火候怎么把握,一开始尝试选取n个点,步长每次减小1/10,精度控制在1e-3,竟然TLE,看了网上的代码,也是这么多。

然后开始各种尝试,果断减小选取的点数。各种WA,TLE无语。。。
有位好心的ACMER,给了一组数据

0 0 
0 2 
2 2 
2 0 
1
一般如果在判断的时候要求精度太高的话,这组数据过不了,果断不原本1e-8的精度改成1e-3,一通乱改之后,可以说是勉强通过了这组数据。
大清早的起来又是刷屏,各种尝试火候,最终还是刷进了200ms
最多选 20个点,每个点走5步,步长为原来的0.55,之前的TLE主要原因就是那组数据出不来,而且会WA。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值