[bzoj1020][SHOI2008]安全的航线flight【迭代】【计算几何】

【题目链接】
  http://www.lydsy.com/JudgeOnline/problem.php?id=1020
【题解】
  首先有一个朴素的想法就是二分,然后判断带圆弧的多边形是否能完全覆盖直线,是不是很简单?
  但我不会 QAQ
  考虑暴力枚举每一个点(隔0.005),求出它到最近陆地的距离,取最大值。
  这样做编程复杂度会下降很多,但会 TLE。
  观察后发现,许多状态是多余的,于是想到剪枝,具体的方法是:
    1.将每一条线段加入队列中。
    2.区出队收线段,找出线段两端的最近点,二分找出距离这两个点最远的在线段上点,设为 mid,若它到这两个点的最近距离 当前答案,就将整个线段删除。
    tips:找 mid 时,它要么在线段最左边,要么在最右边,要么在垂直平分线上,所以二分垂直平分线(直接求也行),然后与左右比较。
    3.用 mid 更新答案。
    4.加入左端点到 midmid 到右端点的线段。(满足长度≥0.005)
    5.重复2-4直到队列为空。
  这种做法能顺利通过此题,复杂度 O(???)
  还是挺好(nan)写的

/* --------------
    user Vanisher
    problem bzoj-1020 
----------------*/
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     1e9
# define    eps     1e-4
# define    anseps  1e-3
# define    N       110
# define    T       5000000
using namespace std;
int num[N],n,m,k;
struct point{
    double x,y;
}mp[N][N],rd[N];
struct seg{
    point a,b;
}l[N*N],q[T];
double ans;
point operator +(point x, point y){return (point){x.x+y.x,x.y+y.y};}
point operator -(point x, point y){return (point){x.x-y.x,x.y-y.y};}
point operator *(point x, double y){return (point){x.x*y,x.y*y};}
point operator /(point x, double y){return (point){x.x/y,x.y/y};}
double sqr(double x){return x*x;}
double dis(point x, point y){
    return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
double dot(point x, point y){
    return x.x*y.x+x.y*y.y;
}
double cross(point x, point y){
    return x.x*y.y-x.y*y.x;
}
double dis_PtoS(point x, seg y){
    if (dot(x-y.a,y.b-y.a)<0) return dis(x,y.a);
    if (dot(x-y.b,y.a-y.b)<0) return dis(x,y.b);
    return fabs(cross(y.a-x,y.b-x))/dis(y.a,y.b);
}
point PtoS(point x, seg y){
    if (dot(x-y.a,y.b-y.a)<0) return y.a;
    if (dot(x-y.b,y.a-y.b)<0) return y.b;
    double d=dis_PtoS(x,y), d1=sqrt(sqr(dis(y.a,x))-sqr(d));
    return y.a+(y.b-y.a)*(d1/dis(y.a,y.b));
}
bool PonS(point x, seg y){
    return fabs(cross(x-y.a,x-y.b))<eps&&dot(x-y.a,x-y.b)<eps;
}
bool ScrossS(seg a, seg b){
    return cross(a.a-b.a,b.b-b.a)*cross(a.b-b.a,b.b-b.a)<eps&&cross(b.a-a.a,a.b-a.a)*cross(b.b-a.a,a.b-a.a)<eps;
}
bool in(point x, int id){
    for (int i=1; i<=num[id]; i++)
        if (PonS(x,(seg){mp[id][i],mp[id][i+1]})==true) 
            return true;
    point y={10001,x.y+0.1}; int cnt=0;
    for (int i=1; i<=num[id]; i++)
        if (ScrossS((seg){x,y},(seg){mp[id][i],mp[id][i+1]})==true) cnt++;
    return cnt%2;
}

int read(){
    int tmp=0, fh=1; char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    return tmp*fh;
}

bool check(point x){
    bool now=false;
    for (int i=1; i<=m; i++){
        now=now|in(x,i);
    }
    return now;
}
point findnear(point x){
    if (check(x)) return x;
    point mn={inf,inf};
    for (int i=1; i<=k; i++){
        point now=PtoS(x,l[i]);
        if (dis(mn,x)>dis(now,x)) mn=now;
    }
    return mn;
} 
void checkans(point x){
    point y=findnear(x);
    ans=max(ans,dis(x,y));
}
int main(){
    m=read(), n=read();
    for (int i=1; i<=n; i++)
        rd[i].x=read(), rd[i].y=read(); 
    for (int i=1; i<=m; i++){
        num[i]=read();
        for (int j=1; j<=num[i]; j++)
            mp[i][j].x=read(), mp[i][j].y=read();
        mp[i][num[i]+1]=mp[i][1];
        for (int j=1; j<num[i]; j++)
            l[++k].a=mp[i][j], l[k].b=mp[i][j+1];
        l[++k].a=mp[i][num[i]], l[k].b=mp[i][1];
    }
    int pl=1, pr=0;
    for (int i=1; i<n; i++)
        q[++pr].a=rd[i],q[pr].b=rd[i+1];
    while (pl<=pr){
        seg now=q[pl++];
        point lp=findnear(now.a), rp=findnear(now.b), l=now.a, r=now.b, mid;
        while (dis(l,r)>anseps){
            mid=(l+r)/2;
            if (dis(mid,lp)>dis(mid,rp)) 
                r=mid; else l=mid;
        }
        checkans(now.a); checkans(now.b); checkans(mid);
        if (dis(mid,lp)<ans+eps) continue; 
        if (dis(now.a,mid)>anseps) q[++pr].a=now.a, q[pr].b=mid;
        if (dis(mid,now.b)>anseps) q[++pr].a=mid, q[pr].b=now.b;
    }
    printf("%.2lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值