zoukankan      html  css  js  c++  java
  • BZOJ2548:[CTSC2002]灭鼠行动

    我对模拟的理解:https://www.cnblogs.com/AKMer/p/9064018.html

    题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2548

    原本计划先在三国杀赢了那几头猪再来灭这群老鼠的……

    然后因为被猪吊锤了一周……然后特工队也等不下去了,我就被调来先灭老鼠了……

    灭完老鼠继续跟猪打三国杀去(emmm)……

    ([SDOI2010])猪国杀和([CTSC2002])灭鼠行动就是我模拟旅程的最后两站了,弄完就不专门搞模拟了。

    由于有[ZJOI2008]杀蚂蚁这题作为前提,我对于这种在地图上杀什么玩意儿的题理解深了很多,所以写灭鼠行动的时候比杀蚂蚁轻松多了。很多人说灭鼠行动比杀蚂蚁难,我觉得并不然。

    首先,让我们把研究的重点放在炸弹上:
    炸弹放下即爆,或者直接晕和变性(听起来好变态)
    定时炸弹相当于一个即爆炸弹,我们可以假装它其实是t+3才放下来的。
    然后就没什么别的了。

    然后我们再把目光放到老鼠身上来:
    老鼠移动算法比蚂蚁移动算法简单多了,能往前就往前,不然就拐弯,拐弯判一判就没了。
    然后繁殖的时候要注意,只有在某个格子上只有一雄一雌两只成年老鼠才会生仔(他们需要私人空间,有其它老鼠在是不会繁殖的)
    然后繁殖以及休息相当于眩晕三秒,第二秒后小老鼠就可以出来了。
    如果在生产过程中被眩晕,那么小老鼠也要推迟时间出来。

    怎么都觉得比杀蚂蚁简单多了……唉,不说了,我跟猪打三国杀去了。

    时间复杂度:(O(Tnm))

    空间复杂度:(O(不会炸))

    代码如下:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define sqr(x) ((x)*(x))
    
    const int inf=2e9;
    
    char s[10];//读入字符串用
    bool explode[55][55];//explode[x][y]表示(x,y)是否在爆炸范围内
    int dx[4]={-1,0,1,0};
    int dy[4]={0,1,0,-1};//四个方向,分别是北,东,南,西,对应编号0,1,2,3,刚好对应读入代表方向的数字2^0,2^1,2^2,2^3
    int left[4]={3,0,1,2};//left[x]表示x方向的左边方向编号
    int right[4]={1,2,3,0};//right[x]表示x方向的右边编号
    int fake[4]={1,2,4,8};//fake[x]表示2^x,也就是读入的网格信息里表示方向的数字
    int now_time,now_bomb=1;//now_time表示现在时间,now_bomb表示现在轮到哪一个炸弹了
    int con[55][55],mice_sum[55][55];//con[x][y]记录(x,y)与四周的连通性,mice_sum[x][y]记录(x,y)上一共多少老鼠
    int len,radius,n,m,tot,limit,Time,cnt;//len记录1号炸弹范围,radius记录2号炸弹范围半径,n,m记录地图大小,tot实时更新记录老鼠个数,limit为评判鼠疫是否爆发的标准,Time是要求模拟的时间,cnt是炸弹个数
    
    int read() {
    	int x=0,f=1;char ch=getchar();
    	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+ch-'0';
    	return x*f;
    }//快读
    
    struct mouse {
    	bool dead,turn_left;
    	int x,y,dir,sex,ver,bre,grow;
    //dead记录老鼠是否死亡,turn_left记录老鼠碰到前方无路左右都有路是否要往左转,x,y记录老鼠坐标,dir记录方向,sex记录性别,ver记录眩晕时间,bre记录小老鼠出生的时间,grow记录还有多久成年
    	
    	void build() {
    		x=read(),y=read();
    		scanf("%s",s);
    		if(s[0]=='N')dir=0;
    		if(s[0]=='E')dir=1;
    		if(s[0]=='S')dir=2;
    		if(s[0]=='W')dir=3;
    		turn_left=1;//奇数次往左拐,所以初始为true
    		scanf("%s",s);
    		if(s[0]=='X')sex=1;
    		bre=-1;
    	}//读入老鼠信息
    
    	void move() {
    		bre=-1;//能移动了说明没有预产期,把预产期恢复成-1
    		if(con[x][y]&fake[dir]) {
    			x+=dx[dir],y+=dy[dir];
    			return;//如果能在原方向上向前走就向前走
    		}
    		if(fake[left[dir]]&con[x][y]) {//如果能向左走
    			if(fake[right[dir]]&con[x][y]) {//如果能向右走
    				if(turn_left)dir=left[dir];
    				else dir=right[dir];//奇数次向左,偶数次向右
    				turn_left^=1;//改变下一次拐弯的方向
    			}
    			else dir=left[dir];//只能向左走,花一单位时间转向
    			return;
    		}
    		if(fake[right[dir]]&con[x][y]) {
    			dir=right[dir];
    			return;//只能向右走,花一单位时间转向
    		}
    	    dir=left[dir];//掉头相当于往左转两次
    	}
    }M[500];
    
    struct bomb {
    	int type,tim,x,y;
    //type记录炸弹类型,tim记录炸弹爆炸时间,x,y记录炸弹坐标
    	bool operator<(const bomb &a)const {
    		return tim<a.tim;
    	}//按时间排序
    	
    	void build() {
    		type=read(),tim=read();
    		x=read(),y=read();
    		if(type==3)tim+=3;//如果是三号炸弹那么时间推迟3个单位
    	}
    
    	void mark_1(int x,int y) {//标记1号炸弹爆炸范围
    		explode[x][y]=1;//炸弹所在点标记
    		for(int i=0;i<4;i++) {//往四个方向走
    			int now_x=x,now_y=y,step_sum=0;
    			//step_sum表示从(x,y)到(now_x,now_y)距离多少
    			while(step_sum<len&&(fake[i]&con[now_x][now_y]))
    				now_x+=dx[i],now_y+=dy[i],explode[now_x][now_y]=1,step_sum++;//如果能继续深入就继续深入
    		}
    	}
    
    	void mark_2(int x,int y) {//2号炸弹标记范围
    		for(int i=1;i<=n;i++)
    			for(int j=1;j<=m;j++)
    				if(sqr(i-x)+sqr(j-y)<=radius)
    					explode[i][j]=1;//勾股定理
    	}
    	
    	void blast() {
    		memset(explode,0,sizeof(explode));//清空标记
    		if(type==1)mark_1(x,y);
    		if(type==2)mark_2(x,y);
    		if(type==3)explode[x][y]=1;
    		if(type==4)explode[x][y]=1;//标记范围
    		for(int i=1;i<=tot;i++)
    			if(explode[M[i].x][M[i].y]) {//如果在范围内
    				if(type==1||type==3)M[i].dead=1;//如果是1号或者3号炸弹直接秒了老鼠
    				if(type==2) {
    					M[i].ver+=3;
    					if(M[i].bre>now_time)M[i].bre+=3;//2号炸弹就加眩晕,如果有预产期预产期也跟着推后
    				}
    				if(type==4)M[i].sex^=1;//4号炸弹变性
    			}
    	}
    }B[105];
    
    void check_dead() {
    	int top=0;
    	for(int i=1;i<=tot;i++)
    		if(!M[i].dead)
    			M[++top]=M[i];
    	tot=top;//清理死亡老鼠尸体
    }
    
    void breed() {
    	memset(mice_sum,0,sizeof(mice_sum));//清空mice_sum[][]
    	for(int i=1;i<=tot;i++)
    		mice_sum[M[i].x][M[i].y]++;//更新mice_sum[][]
    	for(int i=1;i<=tot;i++)
    		if(!M[i].ver&&M[i].bre==-1&&!M[i].grow&&M[i].sex)//如何i号老鼠没有眩晕时间并且可以繁衍,也是成年的雄老鼠
    			for(int j=1;j<=tot;j++)
    				if(!M[j].ver&&M[j].bre==-1&&!M[j].grow&&!M[j].sex)
    				//j号老鼠也是没有眩晕的成年可生育老鼠,并且是雌的	if(mice_sum[M[i].x][M[i].y]==2&&M[i].x==M[j].x&&M[i].y==M[j].y){//如果他们在同一个格子上并且没有其它老鼠在场就开始造老鼠
    						M[i].ver+=3;M[j].ver+=3;//眩晕时间+3
    						M[i].bre=M[j].bre=now_time+2;//2单位时间后生出小老鼠
    						break;
    					}
    	for(int i=1;i<=tot;i++)
    		if(M[i].bre==now_time&&M[i].sex)//如果现在的时间这只老鼠会生育并且他是雄的,每一对老鼠生育只算一次
    			for(int j=0;j<4;j++)
    				if(fake[j]&con[M[i].x][M[i].y]) {//如果该点有该方向的管道那么生成一只小老鼠
    					tot++;
    					M[tot].x=M[i].x;
    					M[tot].y=M[i].y;
    					M[tot].ver=0;
    					M[tot].dir=j;
    					M[tot].grow=5;
    					M[tot].bre=-1;
    					M[tot].dead=0;
    					M[tot].turn_left=1;
    					if(j==0||j==2)M[tot].sex=1;
    					else M[tot].sex=0;
    					//记得所有数据全部都要初始化,因为tot+1保不定存了某只死亡的老鼠的信息,不全部初始化就GG了
    				}
    }
    
    int main() {
    	len=read(),radius=read(),n=read(),m=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			con[i][j]=read();
    	tot=read();radius*=radius;//读入,半径先平方一遍,到时候直接上勾股定理
    	for(int i=1;i<=tot;i++)
    		M[i].build();//读入老鼠信息
    	cnt=read(),limit=read();
    	for(int i=1;i<=cnt;i++)
    		B[i].build();//读入炸弹
    	sort(B+1,B+cnt+1);B[cnt+1].tim=inf;//炸弹排序
    	Time=read();//读入时间
    	for(now_time=0;now_time<=Time;now_time++) {
    		while(B[now_bomb].tim==now_time)
    			B[now_bomb].blast(),now_bomb++;//把现在这个时间会炸的炸弹点爆。每一秒最先发生的事情是炸炸弹而不是老鼠繁殖和移动,这一点我改了好久……把它当杀蚂蚁了……
    		check_dead();//清理尸体
    		breed();//繁殖
    		if(tot>limit) {puts("-1");return 0;}//如果鼠疫爆发就直接没得玩了
    		if(now_time==Time)break;//时间过完了就不移动了。
    		for(int i=1;i<=tot;i++) {
    			if(M[i].ver)
    				M[i].ver--;//如果眩晕就减一秒眩晕时间
    			else {
    				M[i].move();//移动
    				if(M[i].grow)M[i].grow--;//小老鼠长大一秒
    			}
    		}
    	}
    	printf("%d
    ",tot);//输出老鼠总数
    	return 0;
    }
    
  • 相关阅读:
    Windows性能调优: Perfomn.exe 和Perfmon /res
    WPF:逻辑树和视觉树
    AD FS 概述
    SQL Server : TRUSTWORTHY 数据库属性
    WCF:在开发期间应该注意的问题
    ASP.NET MVC 2中的数据验证
    SQL Server:如何在Service Broker发送消息验证失败后获取源消息
    GDI+:自定义控件时如何使用Region来输出特定区域
    LINQ to XML:如何替换XCData的内容
    javascript是否真的就不能实现跨站请求呢?
  • 原文地址:https://www.cnblogs.com/AKMer/p/9165706.html
Copyright © 2011-2022 走看看