1822: [JSOI2010]Frozen Nova 冷冻波

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

题解

这题大概二分下答案然后建图跑网络流
但是难点不是这个,而是判断有没有被树挡住
可能只是对于我而言
由于基本没什么高中数学知识。。于是只能瞎搞

以下ORZTYB

我们知道,其实这道题就是判断一条线段是否与园有交点
如果点到直线的距离都没有的话,那么就肯定没有了
有以下两种情况:
1.点作垂线与直线的交点在线段上
2…….不在线段上

对于第一种情况,我们可以直接用该垂线的长度来当做最短距离判断就可以了
然而对于第二种情况,我们就选取圆心到两个端点较短的一个距离作为最短距离判断

但是我们怎么判断这个交点在哪里呢?

我们来画一张图
这里写图片描述
A,B是要求的线段,O是圆心,C是垂足
我们记OA,OB长的一段叫做H
我们可以发现,如果在线段上,那么,以OC作直角边,H作斜边,另外一条直角边的长度是小于AB的
反之长度则是大于AB的

于是这样就大功告成了。。
至于怎么求出OC,不会的可以自行上网查一下点到直线的距离,我这里就懒得写了

CODE:

#include<cstdio>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int N=205*2;
const int MAX=1<<29;
int n,m,k;
struct qq{int x,y,r,t;}a[N],b[N],c[N];
bool ooo[N][N];//第i个巫妖能不能看到第j个小精灵
double dis (qq a,qq b)//距离的平方 
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void prepare ()
{
    for (int u=1;u<=n;u++)
    {
        for (int i=1;i<=m;i++)//这个巫妖能不能看到这个小精灵 
        {
            if ((int)dis(a[u],b[i])>a[u].r*a[u].r) {ooo[u][i]=false;continue;}
            bool tf=true;
            int x1=a[u].x,y1=a[u].y;
            int x2=b[i].x,y2=b[i].y;
            int A=(y2-y1),B=(x1-x2),C=-(A*x1+B*y1);
            //A*x1+B*y1+C=0
            //A*x2+B*y2+C=0
            //A*x1+B*y1=A*x2+B*y2
            //A*(x1-x2)=B*(y2-y1)
            for (int j=1;j<=k;j++)//这个树木会不会挡住 
            {
                double Dis;
                double d=(abs((double)(A*c[j].x+B*c[j].y+C)/(double)sqrt(A*A+B*B)));//得到圆心到这个节点的距离
                double shen=max(dis(c[j],a[u]),dis(c[j],b[i]));
            //  printf("NO:%d %d %lf %lf %lf\n",u,i,d,shen,dis(a[u],b[i]));
                if (shen-d*d<dis(a[u],b[i])) //在线段上 
                    Dis=d*d;
                else Dis=min(dis(a[u],c[j]),dis(b[i],c[j]));
                if (Dis<c[j].r*c[j].r)
                {
                    tf=false;
                    break;
                }
            }
            //printf("YES:%d %d %d\n",u,i,tf);
            ooo[u][i]=tf; 
        }
    }
    /*for (int u=1;u<=n;u++)
    {
        for (int i=1;i<=m;i++)
            printf("YES:%d ",ooo[u][i]);
        printf("\n");
    }*/
}
struct qy{int x,y,z,last;}s[N*N*2];
int num,last[N];
int st,ed;
void init (int x,int y,int z)
{
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
    swap(x,y);z=0;
    num++;
    s[num].x=x;s[num].y=y;s[num].z=z;
    s[num].last=last[x];
    last[x]=num;
}
int h[N];
bool Bt ()
{
    memset(h,-1,sizeof(h));h[st]=1;
    queue<int> q;
    q.push(st);
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for (int u=last[x];u!=-1;u=s[u].last)
        {
            int y=s[u].y;
            if (s[u].z>0&&h[y]==-1)
            {
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[ed]!=-1;
}
int mymin (int x,int y){return x<y?x:y;}
int dfs (int x,int f)
{
    if (x==ed) return f;
    int s1=0;
    for (int u=last[x];u!=-1;u=s[u].last)
    {
        int y=s[u].y;
        if (s[u].z>0&&h[y]==h[x]+1&&s1<f)
        {
            int lalal=dfs(y,mymin(s[u].z,f-s1));
            s1+=lalal;
            s[u].z-=lalal;
            s[u^1].z+=lalal;
        }
    }
    if (s1==0) h[x]=-1;
    return s1;
}
bool check (int x)//我有这么多时间行不行 
{
    num=1;memset(last,-1,sizeof(last));
    for (int u=1;u<=n;u++) init(st,u,x/a[u].t+1);
    for (int u=1;u<=m;u++) init(u+n,ed,1);
    for (int u=1;u<=n;u++)
        for (int i=1;i<=m;i++)
            if (ooo[u][i])
                init(u,i+n,1);
    int ans=0;
    while (Bt()) ans=ans+dfs(st,MAX);
    return ans==m;
}
void solve ()
{
    for (int u=1;u<=m;u++)
    {
        bool tf=false;
        for (int i=1;i<=n;i++)
            if (ooo[i][u]) tf=true;
        if (tf==false) {printf("-1\n");return ;}
    }
    int l=0,r=MAX;
    int ans;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) {ans=mid;r=mid-1;}
        else l=mid+1;
    }
    printf("%d",ans);
}
void Ins ()
{
    scanf("%d%d%d",&n,&m,&k);st=n+m+1;ed=st+1;
    for (int u=1;u<=n;u++) scanf("%d%d%d%d",&a[u].x,&a[u].y,&a[u].r,&a[u].t);//巫妖 
    for (int u=1;u<=m;u++) scanf("%d%d",&b[u].x,&b[u].y);//小精灵 
    for (int u=1;u<=k;u++) scanf("%d%d%d",&c[u].x,&c[u].y,&c[u].r);//树木
}
int main()
{
    Ins();
    prepare();
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值