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

转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove

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

http://acm.hdu.edu.cn/showproblem.php?pid=3644 

一开始以为是半平面交,果断看大家的提交时间和代码长度就不像是,不过还是提交了一发,果断WA.

后来被昀昀科普了,凹多边形是不可以这么求的。凹多边形可能没有核,向内推近r后求核,显然是不对的。

点不是很多,那就只有模拟退火了,貌似不是只有。。。

要写这题,首先还得写个判断点是否在多边形内,因为以前写得太矬,打算重新写一个,然后就折腾了一晚上,各种错误,老是用直线去代替线段。有了这个判断,便可以开始模拟退火

模拟退火关键问题在于火候怎么把握,一开始尝试选取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。

#include<iostream>
#include<fstream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<ctime>
#include<sstream>
#include<cassert>
#define LL long long
#define eps 1e-7
#define zero(a) fabs(a)<eps
#define inf 1<<30
#define N 20
#define pi acos(-1.0)
using namespace std;
struct Point{
    double x,y;
    double val;
}p[100],tp[100],pre,cur;
struct Segment{
    Point a,b;
};
int n;
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(){
    double r,best[105];
    srand(time(NULL));
    while(scanf("%d",&n)!=EOF&&n){
        double maxx=0,maxy=0,minx=inf,miny=inf;
        for(int i=0;i<n;i++){
            scanf("%lf%lf",&p[i].x,&p[i].y);
            maxx = maxx>p[i].x?maxx:p[i].x;
            maxy = maxy>p[i].y?maxy:p[i].y;
            minx = minx<p[i].x?minx:p[i].x;
            miny = miny<p[i].y?miny:p[i].y;
        }
        p[n]=p[0];
        scanf("%lf",&r);
        int m=min(n,N);
        for(int i=0;i<m;i++){
            tp[i].x=(p[i].x+p[i+1].x)/2;
            tp[i].y=(p[i].y+p[i+1].y)/2;
            tp[i].val=0;
        }
        double step=sqrt((maxx-minx)*(maxx-minx)+(maxy-miny)*(maxy-miny))/2;
        bool flag=false;
        while(step>1e-3&&!flag){
            for(int i=0;i<m&&!flag;i++){
                for(int j=0;j<5&&!flag;j++){
                    double angle=rand();
                    cur.x=tp[i].x+step*cos(angle);
                    cur.y=tp[i].y+step*sin(angle);
                    if(!In_Polygon(cur)) continue;
                    Get_Min_Dist(cur);
                    if(cur.val+1e-3>tp[i].val){
                        tp[i]=cur;
                        if(cur.val+1e-3>r) flag=true;
                    }
                }
            }
            step*=0.55;
        }
        puts(flag?"Yes":"No");
    }
    return 0;
}






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值