zoukankan      html  css  js  c++  java
  • P6474 [NOI Online #2 入门组] 荆轲刺秦王

    P6474 [NOI Online #2 入门组] 荆轲刺秦王

    bfs+差分+卡常
    本来我其实是场内选手,但是因为记错提交时间,晚了半小时才交,交不上了,就自动降级为了场外选手

    题面复杂,不简述了


    首先定义状态 (dis(x,y,num1,num2)) 表示当前坐标是 ((x,y)),然后用了 (num1) 次隐身,(num2) 次瞬移,的最短时间
    答案就是 (min(dis(tx,ty,[0,c1],[0,c2]))),其中 ((tx,ty)) 为终点坐标

    对于每个坐标,枚举 12 个方向
    分别是 8 个正常走路的方向,如果进入了卫兵监视范围,(num1) 要加一,如果走到了卫兵的格子上,就不行
    还有 4 个是瞬移的方向,此时 (num2) 要加一
    同样要考虑是否进入卫兵监视范围和是否走到了卫兵的格子上

    如果发现走到了最终格子,更新答案
    题目要求先最小化时间,再最小化技能次数和,再最小化隐身次数,按要求更新就好,稍显麻烦
    因此,也不能一搜到答案就退出,因为退出以后,后面可能还有花费同样时间,但技能次数小的方案
    如果搜到就直接退出了后面的就取不到了

    那么复杂度显然是 (O(n imes m imes c1 imes c2)),但肯定远远跑不满

    考虑怎么标记卫兵的防护范围,因为是曼哈顿距离,所以手动标记一下会观察出,防护范围就是一个斜着的正方形
    比较像飞飞侠这题里的每一步能走哪些格的描述,当然做法上和这个题没有任何关系

    直接标记是 (n^4),不行
    因为是一个区间,很容易想到使用线段树,对每一行建线段树,然后区间修改,bfs 时单点查询
    但是这样多一个 (log),变成了 (O(n imes m imes c1 imes c2 imes 12 imes log m)approx 2cdot 10^9),即使跑不满也够呛能过
    然后事实证明,跑的是非常不满,只 TLE 了一个点,下面介绍的差分做法在优化之前也是 TLE 一个

    所以因为查询是单点,所以可以使用差分
    比如要修改区间 ([x,y]),让他们区间加一,设差分数组为 (t),则让 (t_x) 加一,(t_{y+1}) 减一
    然后,在对 (t) 做一个前缀和,就是原数组

    为什么?因为 (t_i) 再做前缀和之前,表示的实际上是:(i) 号位置的数,比 (i-1) 位置的数大了多少
    (t_x) 加一,就是 (x) 位置在经过区间加以后,比 (x-1) 位置的数,又多大了 (1)
    ([x+1,y]) 之间的所有位置,并 没有比它前面那个数多大或少大(相对大小没变),所以不用改变
    (t_{y+1}) 减一,就是因为 ([x,y]) 区间加以后,(y+1) 位置的数比 (y) 位置的数 少大了 (1)

    那么,(0) 号位置是 (0),所以 (1) 号位置比它“多大了”多少,那么 (1) 号位置就是多少
    (1) 号位置确定了,(2) 号位置比 (1) 号位置“多大了”多少(在这里是“多大了”(t_2)),那 (2) 号位置实际的值就是 (t_1+t_2),因为 (t_1) 在这里是相当于已经做过前缀和了,表示实际数值
    然后 (t_2) 也就表示实际数值了,就一路前缀和到后面,算出了真实值

    卡常
    不要用STL!!!
    队列一定不要用STL的,慢到死,会被卡在第 18 个点
    还有,如果状态有很多维(比如这个题就是),不要偷懒把状态一个一个入队(我就经常这么做),太慢,最好是构建一个结构体
    以前做的题不卡常看不出来
    STL队列本机 10s+,改成手写本机+结构体 3s 多,但洛谷上还是过不了

    然后加一个最优性剪枝

    if(step>ans) continue;
    

    就是如果当前的步数已经大于当前记录下的最优答案了,就不再继续搜了

    然后本机 1s,洛谷 3s

    代码写的丑了,挺长

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    } 
    int n,m,d,c1,c2;
    int sx,sy,tx,ty;
    int have[355][355],vis[355][355][17][17];
    const int dx[8]={-1,-1,-1,0,0,1,1,1};
    const int dy[8]={-1,0,1,-1,1,-1,0,1};
    const int dxx[4]={-1,1,0,0};
    const int dyy[4]={0,0,1,-1};
    int t[355][355];
    int ans=1e9,ans_num1,ans_num2;
    inline int abs(int x){return x>0?x:-x;}
    inline int min(int x,int y){return x>y?y:x;}
    inline int max(int x,int y){return x>y?x:y;}
    struct data{
    	int x,y,step,num1,num2;
    }q[30000006];
    inline int bfs(){
    	reg int head=1,tail=0;
    	q[++tail]=(data){sx,sy,0,0,0};
    	vis[sx][sy][0][0]=1; 
    	reg int x,y,step,num1,num2;
    	data data_;
    	while(head<=tail){
    		data_=q[head++];
    		x=data_.x;
    		y=data_.y;
    		step=data_.step;
    		num1=data_.num1;
    		num2=data_.num2;
    		if(step>ans) continue;
    		for(reg int x_,y_,k=0;k<8;k++){
    			x_=x+dx[k];y_=y+dy[k];
    			if(x_<1||x_>m) continue;
    			if(y_<1||y_>n) continue;
    			if(have[x_][y_]) continue;
    			if(t[y_][x_]){
    				if(num1==c1) continue;
    				if(vis[x_][y_][num1+1][num2]) continue;
    				if(x_==tx&&y_==ty){
    					if(step+1<ans) ans=step+1,ans_num1=num1+1,ans_num2=num2;
    					else if(step+1==ans){
    						if(num1+num2+1<ans_num1+ans_num2) ans_num1=num1+1,ans_num2=num2;
    						else if(num1+num2+1==ans_num1+ans_num2){
    							if(num1+1<ans_num1) ans_num1=num1+1,ans_num2=num2;
    						}
    					}
    				}
    				vis[x_][y_][num1+1][num2]=1;
    				q[++tail]=(data){x_,y_,step+1,num1+1,num2};
    			}
    			else{
    				if(vis[x_][y_][num1][num2]) continue;
    				if(x_==tx&&y_==ty){
    					if(step+1<ans) ans=step+1,ans_num1=num1,ans_num2=num2;
    					else if(step+1==ans){
    						if(num1+num2<ans_num1+ans_num2) ans_num1=num1,ans_num2=num2;
    						else if(num1+num2==ans_num1+ans_num2){
    							if(num1<ans_num1) ans_num1=num1,ans_num2=num2;
    						}
    					}
    				}
    				vis[x_][y_][num1][num2]=1;
    				q[++tail]=(data){x_,y_,step+1,num1,num2};
    			}
    		}
    		if(num2==c2) continue;
    		for(reg int x_,y_,k=0;k<4;k++){
    			x_=x+dxx[k]*d;y_=y+dyy[k]*d;
    			if(x_<1||x_>m) continue;
    			if(y_<1||y_>n) continue;
    			if(have[x_][y_]) continue;
    			if(t[y_][x_]){
    				if(num1==c1) continue;
    				if(vis[x_][y_][num1+1][num2+1]) continue;
    				if(x_==tx&&y_==ty){
    					if(step+1<ans) ans=step+1,ans_num1=num1+1,ans_num2=num2+1;
    					else if(step+1==ans){
    						if(num1+num2+2<ans_num1+ans_num2) ans_num1=num1+1,ans_num2=num2+1;
    						else if(num1+num2+2==ans_num1+ans_num2){
    							if(num1+1<ans_num1) ans_num1=num1+1,ans_num2=num2+1;
    						}
    					}
    				}
    				vis[x_][y_][num1+1][num2+1]=1;
    				q[++tail]=(data){x_,y_,step+1,num1+1,num2+1};
    			}
    			else{
    				if(vis[x_][y_][num1][num2+1]) continue;
    				if(x_==tx&&y_==ty){
    					if(step+1<ans) ans=step+1,ans_num1=num1,ans_num2=num2+1;
    					else if(step+1==ans){
    						if(num1+num2+1<ans_num1+ans_num2) ans_num1=num1,ans_num2=num2+1;
    						else if(num1+num2+1==ans_num1+ans_num2){
    							if(num1<ans_num1) ans_num1=num1,ans_num2=num2+1;
    						}
    					}
    				}
    				vis[x_][y_][num1][num2+1]=1;
    				q[++tail]=(data){x_,y_,step+1,num1,num2+1};
    			}
    		}
    	}
    	return -1;
    }
    int main(){
    //	std::freopen("bandit18.in","r",stdin);
    //	std::freopen("bandit.out","w",stdout);
    	n=read();m=read();c1=read();c2=read();d=read();
    	reg char c;
    	for(reg int i=1;i<=n;i++){
    		for(reg int j=1;j<=m;j++){
    			c=std::getchar();
    			while(c!='S'&&c!='T'&&c!='.'&&(c<'0'||c>'9')) c=std::getchar();
    			if(c=='S') sx=j,sy=i;
    			else if(c=='T') tx=j,ty=i;
    			else if(c>='0'&&c<='9'){
    				reg int x=c^48;c=std::getchar();
    				while(c>='0'&&c<='9') x=x*10+(c^48),c=std::getchar();
    				have[j][i]=1;
    				int tmp=std::min(n,i+x-1);
    				for(reg int k=std::max(i-x+1,1);k<=tmp;k++)
    					t[k][max(1,j-x+1+abs(i-k))]++,t[k][min(m,j+x-1-abs(i-k))+1]--;
    			}
    		}
    	}
    	for(reg int i=1;i<=n;i++)
    		for(reg int j=1;j<=m;j++) t[i][j]+=t[i][j-1];
    	bfs();
    	if(ans==1e9) std::puts("-1");
    	else std::printf("%d %d %d",ans,ans_num1,ans_num2);
    	return 0;
    }
    
  • 相关阅读:
    赋值运算 就是 +*/ 什么的啦
    利用application,cookies,sessino以及文件文件操作制作计数器和投票的综合实例(按学习进程更新)
    实现二级联动菜单 C#,带完整数据库 SQL
    写入、读取 cookie 无聊顺便复习了下前面学的东西!
    两级联动菜单之ListBox.item.add做法
    利用textbox接收两个数,列出一个数组,并做简单的运算
    .net服务器控件列表19个
    循环显示checkboxlist控件项的时候,count到底要不要 1
    validation验证控件案例以及正则表达式中几个特殊符号的说明!
    上传文件 动作详解(最简单的这种)
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12794613.html
Copyright © 2011-2022 走看看