【BZOJ1043】【HAOI2008】下落的圆盘 计算几何

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/46564199");
}

题解:

给每个圆求一下:
1. 它是不是被之后的某圆整体覆盖。
2. 它的圆周上有哪些弧段被覆盖了。
然后对于每个圆求一下还剩多少周长即可。

上述的“2.”可以用圆的圆心角区间来表示哪些弧段被覆盖。
然后圆心角大小可以用余弦定理求,位置可以取两圆心连线的角度加减其圆心角的一半。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1010
#define eps 1e-7
using namespace std;
const double pi=acos(-1.0);

struct Point
{
    double x,y;
    void read(){scanf("%lf%lf",&x,&y);}
    double operator - (const Point &A)const
    {return sqrt((x-A.x)*(x-A.x)+(y-A.y)*(y-A.y));}
};
struct Circle
{
    Point c;
    double r;
    void read()
    {
        scanf("%lf",&r);
        c.read();
    }
}c[N]; // circles
int cnt[N],n;
struct Segment
{
    double l,r;
    Segment(double _l=0,double _r=0):l(_l),r(_r){}
    bool keep()
    {
        if(r>pi)
        {
            r-=2*pi;
            return 1;
        }
        if(l<-pi)
        {
            l+=2*pi;
            return 1;
        }
        return 0;
    }
    bool operator < (const Segment &A)const
    {return l<A.l;}
}seg[N][N<<1],sg;
int inc(const Circle &A,const Circle &B) // 判断半径和圆心距离
{
    double dis=A.c-B.c;
    if(A.r+B.r<dis)return 0;
    if(A.r+eps>B.r&&A.r-B.r+eps>dis)return -1;
    if(B.r+eps>A.r&&B.r-A.r+eps>dis)return 0;
    return 1;
}
void getinc(const Circle &A,const Circle &B,double &ret1,double &ret2)
{
    double angle=atan2(B.c.y-A.c.y,B.c.x-A.c.x),dis=A.c-B.c;
    double dang=acos((B.r*B.r+dis*dis-A.r*A.r)/(2*dis*B.r));
    ret1=angle-dang,ret2=angle+dang;
}
bool die[N];
double ans;
int main()
{
//  freopen("test.in","r",stdin);

    int i,j,k;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        c[i].read();
        for(j=1;j<i;j++)if(!die[j])if(k=inc(c[i],c[j]))
        {
            if(k==-1)
            {
                die[j]=true;
                continue;
            }
            cnt[j]++;
            getinc(c[i],c[j],seg[j][cnt[j]].l,seg[j][cnt[j]].r);
            if(seg[j][cnt[j]].keep())
            {
                cnt[j]++;
                seg[j][cnt[j]]=Segment(-pi,seg[j][cnt[j]-1].r);
                seg[j][cnt[j]-1].r=pi;
            }
        }
    }
    double remain;
    for(i=1;i<=n;i++)if(!die[i])
    {
        sort(seg[i]+1,seg[i]+cnt[i]+1);
        remain=2*pi,sg.l=sg.r=-remain;
        for(j=1;j<=cnt[i];j++)
        {
            if(seg[i][j].l>sg.r)
            {
                remain-=(sg.r-sg.l);
                sg=seg[i][j];
            }
            else sg.r=max(sg.r,seg[i][j].r);
        }
        remain-=(sg.r-sg.l);
    //  ans+=2*pi*c[i].r*remain/(2*pi);
        ans+=c[i].r*remain;
    }
    printf("%.3lf\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、付费专栏及课程。

余额充值