(全网最详细!)bzoj 2548 灭鼠行动 模拟 解题报告

Description

最近,有一些繁殖力很强的老鼠在下水道非常猖獗,灭鼠特工队正在计划消灭这些老鼠。下水道只有东西方向和南北方向的管道,如图所示。

灭鼠特工队的队员拥有强大的武器。他们将在某些时刻t在某些位置(x,y)放置武器。他们所使用的武器包括:
1. 强力炸弹:它的攻击范围限定在管道内部,是沿竖直和水平方向,离(x,y)的距离不超过L的区域,但是不能穿透下水道壁。它将在放置之后立刻爆炸,且攻击范围内的老鼠将被全部炸死。
2. 神秘射线:它的攻击范围是以(x,y)为圆心,半径为R的圆,而且可以穿透下水道壁。射线在时刻t施放后,将使攻击范围内的所有老鼠立刻陷入昏迷状态,失去知觉,停止一切生理活动,待到第t+3时刻才能恢复(保持失去知觉前的朝向)。如果在昏迷状态中再次受到射线攻击,那么它将再推迟3个时刻恢复。例如,若老鼠在时刻t和时刻t+1个受到一次射线的攻击,则它要昏迷到第t+3+3时刻才能恢复知觉。恢复知觉以后,老鼠将继续以前的生理活动。
3. 定时炸弹:它的攻击范围仅包括(x,y)。它在时刻t放置后,将在第t+3时刻爆炸,爆炸时处在(x,y)点的老鼠将全部被炸死。
4. 生物炸弹:它的攻击范围仅包括(x,y)。它将在放置之后立刻爆炸,使处在(x,y)点的所有老鼠的性别改变(无论大小,雌变成雄,雄变成雌),但不影响老鼠的正常生理活动。
虽然特工队的实力很强,但是老鼠的实力也不容忽视。
我们定义,相邻两个时刻之间是一个时间单位。从t=0时刻开始,每只老鼠就从初始位置向某一初始方向运动。只要前方有管道,如上图中沿方向N到达点A,老鼠就会一直向前走,运动速度为1。否则,如果只有左边或者只有右边有管道,如上图中沿方向E到达点B时,再不能沿原方向继续前进,它就会花费一个时间单位朝该方向原地转动90度,即它将改变方向朝向S。如果它左边和右边都有管道,如上图中沿方向W到达点C,老鼠会回忆这是第几次处于这种情况。如果是第奇数次遇到,它会向左转,第偶数次就向右转。如果它处于一条死路的尽头,如上图中沿方向W到达点D,那么它会花费两个时间单位连续向右转两次,即它将改变方向朝向E。
如果在t时刻某点恰好只有两只老鼠,一只为成年雄老鼠,一只为成年雌老鼠,则它们将会因为进行繁殖而在该点停留两个单位时间,t+2时刻会在该点对每个有管道的方向生出一只朝着该方向的小老鼠,南北方向为雄小老鼠,东西方向为雌小老鼠。如上图中的C点,t时刻恰好只有两只老鼠,它们都已成年且性别相异,那么在第t+2时刻就会在该点生出三只小老鼠,它们分别朝向N、S、E,性别分别是雄性、雄性、雌性。小老鼠一出生就立刻开始移动,而成年老鼠需要再休息一个时间单位,即在t+3时刻继续活动(两只老鼠都保持生育前的朝向)。小老鼠需要成长5个时间单位才会长成为成年老鼠。
特工队现在制定了一套灭鼠计划,其中包括在下水管道放置武器的位置、时间和类型。你需要帮他们计算灭鼠行动的效果,如果在该计划实施的过程中,老鼠的数量超过了某个限定值,就会爆发鼠疫。

Input

第一行为4个整数L R m n(0<=L,R<=10,1<=m,n<=50),其中L代表强力炸弹的有效攻击距离,R代表神秘射线的作用半径,m和n代表下水道平面图的规模。x坐标的范围为[1,m], y坐标的范围为[1,n]。
从第2行到第m+1行为下水道结构图。我们用方向数1代表N(北),用方向数2代表E(东),用方向数4代表S(南),用方向数8代表W(西)。第i+1行的第j个数字ci,j代表点(i,j)处有管道连接的所有方向数之和,如上图中的点B的方向数之和为12。
第m+2行为一个整数K(1<=K<=50),代表时刻0时老鼠的个数(此时老鼠都是成年的)。
第m+3行到第m+K+2行每行描述一只老鼠,包括该老鼠的初始坐标(x,y) (1<=x<=m, 1<=y<=n),朝向(’E’,’S’,’W’,’N’)以及性别(’X’=雄,’Y’=雌)。输入保证每个老鼠都在水管内。
第m+K+3行为两个整数P,Limit(1<=P, Limit<=100),分别表示特工队准备使用的武器个数以及控制鼠疫发生的老鼠数量的极限。
第m+K+4行到第m+K+P+3行每行描述一个武器,包括该武器的类型(1-强力炸弹,2-神秘射线,3-定时炸弹,4-生物炸弹),放置的时刻t(t>=1),放置的坐标(x,y) (1<=x<=m, 1<=y<=n),输入保证武器放置在管道内。武器按照放置的时间不降序排列。
最后一行包含一个整数Time(1<=Time<=1000),表示模拟的结束时刻。Time保证比所有武器的放置时刻大。

Output

包含一个整数。如果爆发了鼠疫,该整数为-1,否则该整数为时刻Time的老鼠数目。

Sample Input

1 1 3 3

6 14 12

7 15 13

3 11 9

3

1 3 W X

1 2 W X

3 3 S X

3 100

1 1 2 2

3 1 3 1

2 2 3 2

10

Sample Output

1

Source

2012国家集训队Round 1 day3

思路

这个国家集训队的模拟太坑了。。。表示长到不想做。。。

用结构体保存老鼠的性别,方向,位置xy,成长度,是否死亡,繁殖的时间,还需等待的时间,第几次遇见分岔路口。

用结构体保存炸弹的类型,位置xy,出现的时间。用一个队列记录下来所有的幸存老鼠,Num二维数组记录每个点上的老鼠数量(每次调用繁殖函数之前清零),map二维数组保存地图。

【炸弹与爆炸】
该什么时候爆炸,就什么时候爆炸。
对于炸弹一,用dfs遍历他的爆炸范围,然后,把范围内的老鼠的状态赋成死亡。
对于炸弹二,把范围内的老鼠的等待时间都+3,然后判断若是在“产后休息”,那就多休息3个时间点。
对于炸弹三,把这个点的所有老鼠状态标记为死亡。
对于炸弹四,把这个点的所有老鼠性别取反。

【主函数】
由于T<=1000,我们可以枚举每一个时间点,先爆炸,再繁殖(想想为什么)。如果泛滥了就输出“-1”,不然就针对每只活着的老鼠进行等待时间–,成长度++,然后移动,每个时间点统计老鼠总数。

【繁殖】
对于每两个老鼠,只要他们都成年,都是可活动状态,都在一个坐标且这个坐标没有别的老鼠,并且他们性别相反,那么就繁殖,并且相应改变等待时间。注意要两个时间才会出生。以上需在每个时间先处理出来,然后再两两枚举老鼠,若可以,就根据新老鼠加入的规则,在队列中加入一个未成年老鼠。

【活动】
Act函数是针对单个老鼠来的。如果这个老鼠是活的并且等待时间为0,那就移动。移动过程中若是有分岔路口,那么就看是第几次遇见,按照规则利用增量数组更新方向与位置。

【注意事项】
要非常机智的先处理炸弹,再处理繁殖,因为有可能某时刻出生的小老鼠被变性了。注意一下眩晕的老鼠不能开始繁殖,两只繁殖完的老鼠如果一次移动后仍然在一起且这个格子没有其他老鼠就可以繁殖,而不是两只在原地不停繁殖。

【吐槽】
要不是因为讲课,我才不做呢
我干嘛做这题啊。。NOIP绝对不可能考这种模拟,不然我吃键盘。

代码

200行。。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=50+3;
const int K=100+5;
int change[4]={1,2,4,8};
int T,cur,L,R,M,n,tot,P,limit,c[N][N],num[N][N],flag[N][N],nowb;
int xx[4]={-1,0,1,0},yy[4]={0,1,0,-1};
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct bomb
{
    int type;//类型 
    int x,y;//位置
    int t;//时间 
}b[K]; 
bool cmp(bomb a,bomb b)
{return a.t<b.t;}
struct mouse
{
    int x,y;//位置 
    int dir;//方向 
    int grow;//成长度 
    int breed;//可否繁殖 
    int wait;//等待时间 
    int sex;//性别 
    int dead;//是否死亡
    int tim;//第几次遇到岔路 
}m[K];
inline int sqr(int x) 
{return x*x;}
void bomb1(bomb t)
{
    memset(flag,0,sizeof(flag));
    for (int i=0;i<4;i++)
    {
        int nx=t.x,ny=t.y,l=0;
        flag[nx][ny]=1;
        while(c[nx][ny]&change[i])
        {
            nx=nx+xx[i],ny=ny+yy[i];
            l++;
            if (l>L) break;//距离不超过L 
            flag[nx][ny]=1;
        }
    }
    for (int i=1;i<=tot;i++)
    if (flag[m[i].x][m[i].y]) m[i].dead=1;//炸到的地方有老鼠,就死了 
}
void bomb2(bomb t)
{
    for (int i=1;i<=tot;i++)
    {
        if (sqr(m[i].x-t.x)+sqr(m[i].y-t.y)<=R)
        {
            m[i].wait+=3;
            if (m[i].breed>cur) m[i].breed+=3;
            //由于是分别判断,所以也要++ 
        }
    }
}
void bomb3(bomb t)
{
    for (int i=1;i<=tot;i++)
    if (m[i].x==t.x&&m[i].y==t.y) m[i].dead=1;//杀了 
}
void bomb4(bomb t)
{
    for (int i=1;i<=tot;i++)
    if (m[i].x==t.x&&m[i].y==t.y) m[i].sex^=1;//取反性别 
}
void expose()
{
    bomb t=b[nowb+1];
    while(t.t==cur)
    {
        nowb++;
        if (t.type==1) bomb1(t);
        if (t.type==2) bomb2(t);
        if (t.type==3) bomb3(t);
        if (t.type==4) bomb4(t);
        t=b[nowb+1];
    }
}
void act(mouse &t)
{
    t.breed=-1;
    int d=t.dir,now=c[t.x][t.y];
    if ((now&change[d])!=0)
    {
        t.x+=xx[d];t.y+=yy[d];
        return ;
    }//转化二进制,前面有路 
    int l=(d+3)%4,r=(d+1)%4,b=(d+2)%4;
    if (now&change[b]) now-=change[b];
    if (now==change[l]) t.dir=l;
    else if (now==change[l]+change[r])
    {
        t.tim^=1;
        if (t.tim) t.dir=l;
        else t.dir=r;
    }
    else t.dir=r;
}
void breed()
{
    int top=0;
    for (int i=1;i<=tot;i++)
    if (!m[i].dead) m[++top]=m[i];//没死 
    tot=top;//更新总量 
    memset(num,0,sizeof(num));//清零,便于接下来更新 
    for (int i=1;i<=tot;i++)
    num[m[i].x][m[i].y]++;//更新 
    for (int i=1;i<=tot;i++)
    if (!m[i].wait&&m[i].breed==-1&&num[m[i].x][m[i].y]==2&&!m[i].sex&&!m[i].grow)
    {
        for (int j=1;j<=tot;j++)
        if (!m[j].wait&&m[i].x==m[j].x&&m[i].y==m[j].y&&m[j].sex&&!m[j].grow)
        {
            m[i].wait+=3;m[j].wait+=3;
            m[i].breed=cur+2;m[j].breed=cur+2;
            break;
        }
    }
    for (int i=1;i<=tot;i++)
    if (m[i].breed==cur&&m[i].sex)
    {
        for (int k=0;k<4;k++)
        if (change[k]&c[m[i].x][m[i].y])
        {
            tot++;
            m[tot].x=m[i].x;m[tot].y=m[i].y;
            m[tot].dead=m[tot].tim=0;
            m[tot].dir=k;
            m[tot].wait=0;m[tot].breed=-1;m[tot].grow=5;
            if (k==0||k==2) m[tot].sex=0;
            else m[tot].sex=1;
        }   
    }       
}
int main()
{
    char ch[5];
    L=read();R=read();M=read();n=read();R=R*R;
    for (int i=1;i<=M;i++)
    for (int j=1;j<=n;j++)
    c[i][j]=read();
    tot=read();
    for (int i=1;i<=tot;i++)
    {
        m[i].x=read(),m[i].y=read();
        scanf("%s",ch);
        if (ch[0]=='N') m[i].dir=0;if(ch[0]=='E')m[i].dir=1;
        if (ch[0]=='S') m[i].dir=2;if(ch[0]=='W')m[i].dir=3;
        scanf("%s",ch);
        if (ch[0]=='Y') m[i].sex=1;
        m[i].breed=-1;
    }//老鼠信息 
    P=read();limit=read();
    for (int i=1;i<=P;i++)
    {
        b[i].type=read();b[i].t=read();b[i].x=read();b[i].y=read();
        if (b[i].type==3) b[i].t+=3;
    }//炸弹信息 
    sort(b+1,b+P+1,cmp);
    T=read();
    for (cur=0;cur<=T;cur++)
    {
        expose();//爆炸 
        breed();//繁殖 
        if (tot>limit) {puts("-1");return 0;}
        if (cur!=T)
        {
            for (int i=1;i<=tot;i++)
            {
                if (m[i].wait!=0) m[i].wait--;//还不能动 
                else 
                {
                    act(m[i]);//动 
                    if (!m[i].wait&&m[i].grow) m[i].grow--;
                    //相反的,成长度++ 
                }
            }
        }   
    }
    printf("%d\n",tot);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值