zoukankan      html  css  js  c++  java
  • BZOJ-1822 Frozen Nova 冷冻波 计(jie)算(xi)几何+二分+最大流判定+经典建图

    这道逼题!感受到了数学对我的深深恶意(#‵′)。。。。
    

    1822: [JSOI2010]Frozen Nova 冷冻波
    Time Limit: 10 Sec Memory Limit: 64 MB
    Submit: 1282 Solved: 382
    [Submit][Status][Discuss]

    Description
    WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。 当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。 在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。 现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?

    Input
    输入文件第一行包含三个整数N、M、K(N,M,K<=200),分别代表巫妖的数量、小精灵的数量和树木的数量。 接下来N行,每行包含四个整数x, y, r, t,分别代表了每个巫妖的坐标、攻击范围和施法间隔(单位为秒)。 再接下来M行,每行两个整数x, y,分别代表了每个小精灵的坐标。 再接下来K行,每行三个整数x, y, r,分别代表了每个树木的坐标。 输入数据中所有坐标范围绝对值不超过10000,半径和施法间隔不超过20000。

    Output
    输出一行,为消灭所有小精灵的最短时间(以秒计算)。如果永远无法消灭所有的小精灵,则输出-1。

    Sample Input
    2 3 1
    -100 0 100 3
    100 0 100 5
    -100 -10
    100 10
    110 11
    5 5 10

    Sample Output
    5

    HINT

    Source
    JSOI2010第二轮Contest1

    巫(污)妖!这里写图片描述

    这道题啊,污!
    

    建图:
    超级源向所有巫妖建图,边权为time/nova【i】.t+1(二分出来的总时间,除去这个巫妖的施法冷却+1(0s可以释放第一个波))
    每个巫妖和它所能打到得小精灵连边,边权为1
    所有小精灵与超级汇连边,边权为1(因为每个小精灵只能被杀死1次)
    然后二分(left=0,right=maxtime*m冷却最长的巫妖独自杀死所有小精灵的时间一定是最大的)总时间,建图,跑最大流判定即可

    下面是计算(此处采用解析几何,因为人傻)几何部分关于判定巫妖是否能打到小精灵的判定:
    巫妖有攻击半径r,所以能打到这个小精灵的要求起码,这个巫妖和这个精灵之间的距离要<=这个巫妖的施法半径:
    平面间两点距离公式:这里写图片描述
    满足上述条件后还需要满足不被树挡住才能攻击到(在树中会失去视野。。。这个故事提示我们要带眼)
    那么这个时候思考到,我们已知两点的坐标,树的坐标和树的半径
    那么只要满足树的这个圆与这两点连成的直线没有交点即可。如果点到线段的最短距离都大于半径那一定不会相交。
    于是转化成求点到线段的最短距离。
    值得注意的是,这里是点到线段的最短距离,而非直线,所以不能直接套公式,应该理性的分类思考一下:
    1.点的投影正好落在线段上,此时最短距离为这个垂线段的长度,求法与点到直线距离一样。
    2.点的投影在线段的延长线上,这时最短距离为这个点到线段两端点的长度中较短的那条,由第一个条件公式得到即可。
    那么如何判断点的投影是否在线段上呢?
    这里采用的是,先用两点距离公式算出树、巫妖、小精灵三个点组成的三角形的三边长度,然后利用海伦公式求出面积S
    海伦公式及推导:这里写图片描述
    然后在用点到直线距离公式求出,树这个点到这个线段所在直线的距离(垂线段)
    点到直线的距离公式:这里写图片描述
    然后用树到两个端点中较大的那个端点为斜边,勾股出这个点在直线上的投影点到那个端点的距离,判断这个距离和线段的长度,如果线段长度长,则这个点在线段上return 点到直线的距离,否则return点到两个端点的距离中较小的那个距离
    判断如果不被挡住,则可以攻击到,建图完成上述操作即可

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define M 1000010
    int dis[M];
    int q[M],h,t;
    struct data{
        int next,to,v;
    }edge[M];
    int head[M],cnt=1;
    struct data1{
        int x,y,r,t;
    }nova[300];
    struct data2{
        int x,y;
    }fairy[300];
    struct data3{
        int x,y,r;
    }tree[300];
    int n,m,k;
    int maxt=0;
    
    void add(int u,int v,int w)
    {
        cnt++;
        edge[cnt].next=head[u];
        head[u]=cnt;
        edge[cnt].to=v;
        edge[cnt].v=w;
    }
    
    double min(double x,double y)
    {
        if (x>y) return y;
            else return x;
    }
    
    double max(double x,double y)
    {
        if (x>y) return x;
            else return y;
    }
    
    double dist(int x1,int y1,int x2,int y2)
    {
            return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    }//两点之间的距离 
    
    double Helen(double a,double b,double c)
    {
        double q=(a+b+c)/2;
        return sqrt(q*(q-a)*(q-b)*(q-c));
    }//海伦公式求面积 
    
    double gg(double x,double y)
    {
        return sqrt(x*x-y*y);
    }//勾股定理 
    
    double mindis(int x1,int y1,int x2,int y2,int x3,int y3)
    {
        double d1=(fabs((y2-y1)*x3+(x1-x2)*y3+(x2*y1)-(x1*y2)))/(sqrt((y2-y1)*(y2-y1)+(x1-x2)*(x1-x2)));
        double d2=dist(x1,y1,x3,y3);
        double d3=dist(x2,y2,x3,y3);
        double s=Helen(d2,d3,dist(x1,y1,x2,y2));
        double x=gg(max(d2,d3),d1);
        if (x>dist(x1,y1,x2,y2))
            return min(d2,d3);
        else
            return d1;
    }//点到线段的最短距离(树的圆心 到 巫妖和小精灵组成的线段 的距离) 
    //(x1,x2)表示巫妖的坐标,(x2,y2)表示小精灵的坐标,(x3,y3)表示树的坐标
    
    void init()
    {
        scanf("%d%d%d",&n,&m,&k);
        for (int i=1; i<=n; i++)
            {
                scanf("%d%d%d%d",&nova[i].x,&nova[i].y,&nova[i].r,&nova[i].t);
                maxt=max(maxt,nova[i].t);
            }
        for (int i=1; i<=m; i++)
            scanf("%d%d",&fairy[i].x,&fairy[i].y);
        for (int i=1; i<=k; i++)
            scanf("%d%d%d",&tree[i].x,&tree[i].y,&tree[i].r);
    }
    
    void make(int ans)
    {
        //只要一个巫妖和一个小精灵之间的距离小于等于巫妖的施法范围 &&
        //周围的树到这个线段的最短距离大于树的半径  就可以打到
        //超级源点记为0,超级汇记为N+M+1,巫妖的编号从1~N,小精灵从N+1~N+M
        cnt=1;memset(head,0,sizeof(head));
        for (int i=1; i<=n; i++)
            for (int j=1; j<=m; j++)
                if (dist(nova[i].x,nova[i].y,fairy[j].x,fairy[j].y)<=1.0*nova[i].r)
                    {
                        bool f=true;
                        for (int l=1; l<=k; l++)
                            if (mindis(nova[i].x,nova[i].y,fairy[j].x,fairy[j].y,tree[l].x,tree[l].y)<=1.0*tree[l].r)
                                {
                                    f=false;
                                    break;
                                }
                        if (f==false)
                            continue;
                        add(i,n+j,1); add(n+j,i,0);
                    }
        for (int i=1; i<=n; i++)
            {
                add(0,i,ans/nova[i].t+1);
                add(i,0,0);
            }
        for (int j=1; j<=m; j++)
            {
                add(n+j,n+m+1,1);
                add(n+m+1,n+j,0);
            }
    }
    
    bool bfs()
    {
        memset(dis,-1,sizeof(dis));
        q[1]=0; dis[0]=1;
        h=0;    t=1;
        while (h<t)
            {
                int j=q[++h],i=head[j];
                while (i)
                    {
                        if (dis[edge[i].to]<0 && edge[i].v>0)
                            {
                                dis[edge[i].to]=dis[j]+1;
                                q[++t]=edge[i].to;
                            }
                        i=edge[i].next;
                    }
            }
        if (dis[n+m+1]>0)
            return true;
        else
            return false;   
    }
    
    int dfs(int loc,int low)
    {
        int flow=0;
        if (loc==n+m+1) return low;
        int i=head[loc];
        while (i)
            {
                if (edge[i].v>0 && dis[edge[i].to]==dis[loc]+1 && (flow=dfs(edge[i].to,min(low,edge[i].v))))
                    {
                        edge[i].v-=flow;
                        edge[i^1].v+=flow;
                        return flow;
                    }
                i=edge[i].next;
            }   
        return 0;
    }
    
    int main()
    {
        init();
        int ans;
        int answer=-1;
        int left=0,right=maxt*m,mid;
        while (left<=right)
            {
                mid=(left+right)>>1;
                make(mid);
                ans=0;
                while (bfs())
                    {
                        int now=0;
                        while ((now=dfs(0,0x7fffffff)))
                            ans+=now;
                    }
                if (ans==m)
                    {answer=mid; right=mid-1;}
                else
                    left=mid+1;
            }
        printf("%d",answer);
        return 0;
    }
  • 相关阅读:
    操作系统——理论知识
    BEGIN-4 Fibonacci数列
    BEGIN-3 圆的面积
    面向对象三大特征之一:多态
    面向对象三大特征之二:继承
    package---包
    面向对象三大特征之一:封装
    关键字:This(上)
    无参构造与有参构造
    面向对象
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5346229.html
Copyright © 2011-2022 走看看