bzoj 1822: [JSOI2010]Frozen Nova 冰霜新星

魔兽争霸游戏算法挑战
本文探讨了一道关于《魔兽争霸》游戏的算法题目,通过几何、二分及网络流等技术来解决巫妖如何有效消灭小精灵的问题。文章提供了一个详细的解决方案,并附带完整的源代码。

题意:

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

题解:

几何+二分+网络流。
这题很容易想到二分+网络流求答案。
但是难在怎么判断巫妖能不能打到011
注意,树木很粗壮。
首先有三种情况。
这里写图片描述
好吧也可以说是两种。
点到直线距离公式:

d=|x0+By0+CA2+B2|

详细见百度。
对于第一种情况,直接带公式就行了。
但第二种情况就应该取到端点的最短值。
所以我们还要求出点到两端点的较大值。
勾股求第三边。
这里写图片描述
假如这条边的长度小于线段长,那么在是第一种情况,否则为第二种情况。
然后就愉快的建图+跑流了。
code:
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
struct node{
    int x,y,next,c,other;
}a[2500000];int last[500],len=0;
int h[500],s[500],st,ed;
void ins(int x,int y,int c)
{
    int k1=++len;
    a[len].x=x;a[len].y=y;a[len].c=c;
    a[len].next=last[x];last[x]=len;
    int k2=++len;
    a[len].x=y;a[len].y=x;a[len].c=0;
    a[len].next=last[y];last[y]=len;
    a[k1].other=k2;a[k2].other=k1;
}
bool bt_h()
{
    memset(h,0,sizeof(h));
    int l=1,r=2;s[l]=st;h[st]=1;
    while(l!=r)
    {
        int x=s[l];
        for(int i=last[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(h[y]==0&&a[i].c>0) h[y]=h[x]+1,s[r++]=y;
        }
        l++;
    }
    return h[ed]!=0;
}
int findflow(int x,int f)
{
    if(x==ed) return f;
    int t,ans=0;
    for(int i=last[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(h[x]+1==h[y]&&a[i].c>0&&ans<f)
        {
            ans+=(t=findflow(y,min(a[i].c,f-ans)));
            a[i].c-=t;a[a[i].other].c+=t;
        }
    }
    if(ans==0) h[x]=0;
    return ans;
}
struct point{
    int x,y;
}p1[210],p2[210],p3[210];
int R[210];
struct NODE{
    int r,t;
}wy[210];
int n,m,k;
bool check[210][210],kill[210];
/*int multi(point p1,point p2,point p0)
{
    int x1=p1.x-p0.x,x2=p2.x-p0.x;
    int y1=p1.y-p0.y,y2=p2.y-p0.y;
    return x1*y2-x2*y1;
}*/
double cz(int A,int B,int C,point p){return abs((double)(A*p.x+B*p.y+C)/sqrt((double)A*A+(double)B*B));}
double dis(point p1,point p2){return sqrt((double)(p1.x-p2.x)*(p1.x-p2.x)+(double)(p1.y-p2.y)*(p1.y-p2.y));}
bool shoot(point p1,point p2,int r)
{
    double LEN=dis(p1,p2);
    if(LEN>(double)r) return false;
    int A=p2.y-p1.y,B=p1.x-p2.x;
    int C=-(A*p1.x+B*p1.y);
    for(int i=1;i<=n;i++)
    {
        /*double tmp;
        if(multi(p1,p2,p3[i])==0)
        else*/
        double aa=cz(A,B,C,p3[i]),cc=max(dis(p1,p3[i]),dis(p2,p3[i])),t;
        if(sqrt(cc*cc-aa*aa)>LEN) t=min(dis(p1,p3[i]),dis(p2,p3[i]));
        else t=aa;
        if(t<R[i]) return false;
    }
    return true;
}
bool CHECK(int t)
{
    memset(last,0,sizeof(last));len=0;
    st=0;ed=n+m+1;
    for(int i=1;i<=n;i++)
    {
        ins(st,i,t/wy[i].t+1);
        for(int j=1;j<=m;j++)
            if(check[i][j]) ins(i,j+n,1);
    }
    for(int i=1;i<=m;i++) ins(i+n,ed,1);
    int ans=0;
    while(bt_h()) ans+=findflow(st,999999999);
    return ans==m;
}
int main()
{
    scanf("%d %d %d",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%d %d %d %d",&p1[i].x,&p1[i].y,&wy[i].r,&wy[i].t);
    for(int i=1;i<=m;i++) scanf("%d %d",&p2[i].x,&p2[i].y);
    for(int i=1;i<=k;i++) scanf("%d %d %d",&p3[i].x,&p3[i].y,&R[i]);
    memset(check,false,sizeof(check));
    memset(kill,false,sizeof(kill));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(shoot(p1[i],p2[j],wy[i].r)) check[i][j]=true,kill[j]=true;
    /*for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++) printf("%d ",check[i][j]);
        printf("\n");
    }*/
    for(int i=1;i<=m;i++)
        if(!kill[i]){printf("-1");return 0;}
    int l=0,r=5000000,ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(CHECK(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值