拯救公主_广搜_Java实现_解决Runtime error和Wrong Answer

恭喜博主自己花了两天时间终于Accept的算法。!!!!!!!!
在这里插入图片描述
在这里插入图片描述

这种答案会(runtime error)

package 蓝桥杯;
import java.util.Scanner;


public class VO广搜_拯救公主 {

	static String[]  map;//其中.表示位置安全,S代表阿福。E代表公主,$表示传送门,#表示禁止。
	//考虑到保存路径,所以就没有直接用队列,
	//而是用数组模拟的队列,这样能够将东西都保存起来而不弹出
	static step[] queue;
	static int[][] move = {{0,1},{0,-1},{1,0},{-1,0}};//移动的方向右左下上
	static int[][][] visit;//判重
	static int time;//需要花费的时间
	static class step{
		int x,y,pre;//pre存储父节点 的数组下标作为指针
		int k;//宝石的个数
		Boolean[] IsHaveBaoShi;//宝石的获取情况,不能放在全局变量上面,因为其他路可能获取了不同种类的宝石改变他的取值。
		public step(int x,int y,int pre,int k ,Boolean[] have) {
			this.x=x;
			this.y=y;
			this.pre=pre;
			this.k=k;
			this.IsHaveBaoShi=have;
		}
		public int getX() {
			return x;
		}
		public int getY() {
			return y;
		}
		public int getPre() {
			return pre;
		}
		public int getK() {
			return k;
		}
		public Boolean[] getIsHaveBaoShi() {
			return IsHaveBaoShi;
		}	
	}
	static class chuan{
		int nx;
		int ny;
		public  chuan(int nx,int ny) {
			this.nx=nx;
			this.ny=ny;
		}
		public int getNx() {
			return nx;
		}
		public int getNy() {
			return ny;
		}
		
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		int T=reader.nextInt();//表示一共有T组数据
		while(T!=0) {
		time=0;
		int N1=reader.nextInt();//表示R
		int N2=reader.nextInt();//表示C
		int k=reader.nextInt();//需要集齐K种宝石才能打开拘禁公主的结界
		int afuX=0,afuY=0,gongzhuX=0,gongzhuY=0;//阿福S和公主E的位置
		queue=new step[210*210];//疑问:队列的个数为什么最大为N1*N2?相当于递归回溯难道不应该会有很多结点吗?
		visit=new int[N1][N2][15];
		map=new String[N1];
		//存放传输门的队列
		chuan[] chuanqueue= new chuan[15];
		int countchuan=0;
		//对地图中的阿福和公主进行定位
		for(int i=0;i<N1;i++) {
				map[i]=reader.next();//读取每行的字符串
				for(int j=0;j<N2;j++) {
					if(map[i].charAt(j)=='S') {
						afuX=i;
						afuY=j;
					}
					if(map[i].charAt(j)=='E') {
						gongzhuX=i;
						gongzhuY=j;
					}
					if(map[i].charAt(j)=='$') {
						chuan men=new chuan(i,j);
						chuanqueue[countchuan]=men;
						countchuan++;
					}
				}
					
		}
		
		int head=0,tail=0;
		Boolean[] tBoolean=new Boolean[] {false,false,false,false,false};
		queue[head]=new step(afuX, afuY,-1,0,tBoolean);//还差k个宝石
		visit[afuX][afuY][0]=1;
		tail++;
		boolean flag=false;
		while(head<tail) {
//			System.out.println("第"+head+"层: ");
			flag=false;
			for(int i=0;i<4;i++) {
				int nx=queue[head].getX()+move[i][0];
				int ny=queue[head].getY()+move[i][1];
				//1、可行性剪枝:保证不越界
				if(nx >= 0 && nx < N1 && ny >= 0 && ny < N2&&map[nx].charAt(ny)!='#') {
					char current=map[nx].charAt(ny);
					int currenttoint=current-'0';
					//如果是宝石
					if(current=='0'||current=='1'||current=='2'||current=='3'||current=='4') {
						//没有获取过的一种宝石
						if(!queue[head].getIsHaveBaoShi()[currenttoint]&&visit[nx][ny][queue[head].getK()+1]==0){//
							//拷贝一个新的数组(这一点非常重要!!!!!!)
							Boolean[] copyarray=new Boolean[] {false,false,false,false,false};
							System.arraycopy(queue[head].getIsHaveBaoShi(), 0, copyarray, 0, 5);
							copyarray[currenttoint]=true;//将这种宝石标记为已经获取。
							visit[nx][ny][queue[head].getK()+1]=1;
//							System.out.print("("+nx+","+ny+","+head+","+(queue[head].getK()+1)+",)");
							queue[tail]=new step(nx, ny, head,queue[head].getK()+1,copyarray);
							tail++;
						}
						else if(visit[nx][ny][queue[head].getK()]==0){
							visit[nx][ny][queue[head].getK()]=1;
//							System.out.print("("+nx+","+ny+","+head+","+queue[head].getK()+",)");
							queue[tail]=new step(nx, ny, head,queue[head].getK(),queue[head].getIsHaveBaoShi());
							tail++;
						}
					}
					 //如果为传送门$,
					if (current=='$'&&visit[nx][ny][queue[head].getK()]==0) {
						visit[nx][ny][queue[head].getK()]=1;
						for(int j=0;j<countchuan;j++)
						queue[tail]=new step(chuanqueue[j].getNx(), chuanqueue[j].getNx(), head,queue[head].getK(),queue[head].getIsHaveBaoShi());
						tail++;
					}
					 //若为.或者E,并且没访问过并且没访问过 可访问 
					else if(visit[nx][ny][queue[head].getK()]==0) {
							visit[nx][ny][queue[head].getK()]=1;
//							System.out.print("("+nx+","+ny+","+head+","+queue[head].getK()+",)");
							queue[tail]=new step(nx, ny, head,queue[head].getK(),queue[head].getIsHaveBaoShi());
							tail++;
						}
					
				}
				if(nx==gongzhuX&&ny==gongzhuY&&queue[tail-1].getK()>=k) {
					flag=true;
					break;
				}
			}
			if(flag) {
				print(queue[tail-1]);
				System.out.println(time);
				break;
			}
			head++;//假装弹出队列
		}
		if(!flag)System.out.println("oop!");
		T--;
		}
	}
	static void  print(step s) {
		if(s.pre==-1) {
//			System.out.println("("+s.getX()+", "+s.getY()+", "+s.getK()+")");//这是打印路径
			return;
		}
		else {
			print(queue[s.getPre()]);
			time++;
//			System.out.println("("+s.getX()+", "+s.getY()+", "+s.getK()+")");//这是打印路径
		}
	}

}

以上程序出现了runtime error

原因是:数据规模可能超过了 模拟队列结构的自定义数组‘queue’范围,改了之后又会出现WrongAnswers,既然这个问题不需要打印路径信息,就可以和迷宫问题一样,使用java默认的队列接口

经过改进之后,这种答案(WrongAnswer)

原因:判重出现了错误,我拿着1和2经过某个点,和拿着3和4经过某个点同样是两个宝石但是这两个状态我当作一个状态处理了,那就会出现错误,思考之后发现只有用位操作会比较方便快速而且节省空间。
共有32种情况:
00000
00001
00010
00011
.
.
.
11111
这就需要使用二进制压缩的方法表示每一步的状态了。

package 蓝桥杯;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;



public class VO广搜_拯救公主 {

	static String[]  map;//其中.表示位置安全,S代表阿福。E代表公主,$表示传送门,#表示禁止。
	static int[][] move = {{0,1},{0,-1},{1,0},{-1,0}};//移动的方向右左下上
	static int[][][] visit;//判重
	static int time;//需要花费的时间
	static class step{
		int x,y,pre;//pre存储父节点 的数组下标作为指针
		int k;//宝石的个数
		Boolean[] IsHaveBaoShi;//宝石的获取情况,不能放在全局变量上面,因为其他路可能获取了不同种类的宝石改变他的取值。
		public step(int x,int y,int pre,int k ,Boolean[] have) {
			this.x=x;
			this.y=y;
			this.pre=pre;
			this.k=k;
			this.IsHaveBaoShi=have;
		}
		public int getX() {
			return x;
		}
		public int getY() {
			return y;
		}
		public int getPre() {
			return pre;
		}
		public int getK() {
			return k;
		}
		public Boolean[] getIsHaveBaoShi() {
			return IsHaveBaoShi;
		}	
	}
	static class chuan{
		int nx;
		int ny;
		public  chuan(int nx,int ny) {
			this.nx=nx;
			this.ny=ny;
		}
		public int getNx() {
			return nx;
		}
		public int getNy() {
			return ny;
		}
		
	}
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		int T=reader.nextInt();//表示一共有T组数据
		while(T!=0) {
		time=0;
		int N1=reader.nextInt();//表示R
		int N2=reader.nextInt();//表示C
		int k=reader.nextInt();//需要集齐K种宝石才能打开拘禁公主的结界
		int afuX=0,afuY=0,gongzhuX=0,gongzhuY=0;//阿福S和公主E的位置
		Queue<step> queue = new LinkedList<step>();
		visit=new int[N1][N2][6];
		map=new String[N1];
		//存放传输门的队列
		chuan[] chuanqueue= new chuan[15];
		int countchuan=0;
		//对地图中的阿福和公主进行定位
		for(int i=0;i<N1;i++) {
				map[i]=reader.next();//读取每行的字符串
				for(int j=0;j<N2;j++) {
					if(map[i].charAt(j)=='S') {
						afuX=i;
						afuY=j;
					}
					if(map[i].charAt(j)=='E') {
						gongzhuX=i;
						gongzhuY=j;
					}
					if(map[i].charAt(j)=='$') {
						chuan men=new chuan(i,j);
						chuanqueue[countchuan]=men;
						countchuan++;
					}
				}
					
		}
		
		Boolean[] tBoolean=new Boolean[] {false,false,false,false,false};
		queue.offer(new step(afuX, afuY,0,0,tBoolean));//还差k个宝石
		visit[afuX][afuY][0]=1;
		boolean flag=false;
		while(!queue.isEmpty()) {
//			System.out.println("第"+head+"层: ");
			flag=false;
			for(int i=0;i<4;i++) {
				int nx=queue.element().getX()+move[i][0];
				int ny=queue.element().getY()+move[i][1];
				//1、可行性剪枝:保证不越界
				if(nx >= 0 && nx < N1 && ny >= 0 && ny < N2&&map[nx].charAt(ny)!='#') {
					char current=map[nx].charAt(ny);
					int currenttoint=current-'0';
					//如果是宝石
					if(current=='0'||current=='1'||current=='2'||current=='3'||current=='4') {
						//没有获取过的一种宝石
						if(!queue.element().getIsHaveBaoShi()[currenttoint]&&visit[nx][ny][queue.element().getK()+1]==0){//
							//拷贝一个新的数组(这一点非常重要!!!!!!)
							Boolean[] copyarray=new Boolean[] {false,false,false,false,false};
							System.arraycopy(queue.element().getIsHaveBaoShi(), 0, copyarray, 0, 5);
							copyarray[currenttoint]=true;//将这种宝石标记为已经获取。
							visit[nx][ny][queue.element().getK()+1]=1;
//							System.out.print("("+nx+","+ny+","+head+","+(queue[head].getK()+1)+",)");
							queue.add(new step(nx, ny, (queue.element().getPre()+1),queue.element().getK()+1,copyarray));
						}
						else if(visit[nx][ny][queue.element().getK()]==0){
							visit[nx][ny][queue.element().getK()]=1;
//							System.out.print("("+nx+","+ny+","+head+","+queue[head].getK()+",)");
							queue.add(new step(nx, ny, (queue.element().getPre()+1),queue.element().getK(),queue.element().getIsHaveBaoShi()));
						}
					}
					 //如果为传送门$,
					if (current=='$'&&visit[nx][ny][queue.element().getK()]==0) {
						visit[nx][ny][queue.element().getK()]=1;
						for(int j=0;j<countchuan;j++)
						queue.add(new step(chuanqueue[j].getNx(), chuanqueue[j].getNx(), (queue.element().getPre()+1),queue.element().getK(),queue.element().getIsHaveBaoShi()));
					}
					 //若为.或者E,并且没访问过并且没访问过 可访问 
					else if(visit[nx][ny][queue.element().getK()]==0) {
							visit[nx][ny][queue.element().getK()]=1;
//							System.out.print("("+nx+","+ny+","+head+","+queue[head].getK()+",)");
							queue.add(new step(nx, ny, (queue.element().getPre()+1),queue.element().getK(),queue.element().getIsHaveBaoShi()));
						}
					
				}
				if(queue.element().getX()==gongzhuX&&queue.element().getY()==gongzhuY&&queue.element().getK()>=k) {
					flag=true;
					break;
				}
			}
			if(flag) {
				System.out.println(queue.element().getPre());
				break;
			}
			queue.poll();
		}
		if(!flag)System.out.println("oop!");
		T--;
		}
	}

}

这种(Accept)

代码基本上是推翻了重新写过的,改动的地方很多。


import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

/*
 * 自定义传送门数据 ,结果为9
1
7 8 2
........
..S..#0.
.##..1.$
.0#.....
...1#...
...##E$.
...1....
*/
public class Main {

	static String[]  map;//其中.表示位置安全,S代表阿福。E代表公主,$表示传送门,#表示禁止。
	static int[][] move = {{0,1},{0,-1},{1,0},{-1,0}};//移动的方向右左下上
	static int[][][] visit;//判重
	static class step{
		int x,y,pre;//迷宫的坐标及走到这个坐标需要的步数
		int k;//宝石种类的数量,用00000位的二进制数表示32种不同情况
		public step(int x,int y,int pre,int k) {
			this.x=x;
			this.y=y;
			this.pre=pre;
			this.k=k;
		}
		public int getX() {
			return x;
		}
		public int getY() {
			return y;
		}
		public int getPre() {
			return pre;
		}
		public int getK() {
			return k;
		}
		public void setK(int k) {
			this.k = k;
		}
		
	}
	static class chuan{
		int nx;
		int ny;
		public  chuan(int nx,int ny) {
			this.nx=nx;
			this.ny=ny;
		}
		public int getNx() {
			return nx;
		}
		public int getNy() {
			return ny;
		}
		
	}
	
	public static void main(String[] args) {
		Scanner reader=new Scanner(System.in);
		int T=reader.nextInt();//表示一共有T组数据
		while(T!=0) {
			int N1=reader.nextInt();//表示R
			int N2=reader.nextInt();//表示C
			int k=reader.nextInt();//需要集齐K种宝石才能打开拘禁公主的结界
			int afuX=0,afuY=0,gongzhuX=0,gongzhuY=0;//阿福S和公主E的位置
			Queue<step> queue = new LinkedList<step>();
			visit=new int[N1][N2][1<<5]; //宝石数目最大11111
			map=new String[N1];
			//统计传送门的数组和个数,传送门最多10个
			chuan[] chuanqueue= new chuan[11];
			int countchuan=0;
			//对地图中的阿福和公主进行定位
			for(int i=0;i<N1;i++) {
					map[i]=reader.next();//读取每行的字符串
					for(int j=0;j<N2;j++) {
						if(map[i].charAt(j)=='S') {
							afuX=i;
							afuY=j;
						}
						if(map[i].charAt(j)=='E') {
							gongzhuX=i;
							gongzhuY=j;
						}
						if(map[i].charAt(j)=='$') {
							chuan men=new chuan(i,j);
							chuanqueue[countchuan]=men;
							countchuan++;
						}
					}
						
			}
			//从阿福的位置开始入队
			queue.offer(new step(afuX, afuY,0,0));//走到这个位置需要的单位时间为0,还差k-0个宝石
			visit[afuX][afuY][0]=1;
			boolean flag=false;
			while(!queue.isEmpty()) {
				flag=false;
				step current=queue.element();
				queue.poll();
				if(current.getX()==gongzhuX&&current.getY()==gongzhuY&&current.getK()>=(1<<k)-1) {
					System.out.println(current.getPre());
					flag=true;
					break;
				}else if(current.getX()==gongzhuX&&current.getY()==gongzhuY&&current.getK()>=(1<<k)-1){
					continue;//没有这句的话,有可能在路过公主门口继续扩展下去,就没什么必要,最优性剪枝
				}
				
				for(int i=0;i<4;i++) {
					int nx=current.getX()+move[i][0];
					int ny=current.getY()+move[i][1];
					//1、可行性剪枝:保证不越界由于嵌套的if太多了,所以采用continue的方法可以美化代码
					if(nx < 0 || nx >= N1 || ny < 0 || ny >= N2||map[nx].charAt(ny)=='#') continue;
						char currentchar=map[nx].charAt(ny);
						int baoshi=current.getK();
						
						//1、如果是宝石
						if(currentchar>='0'&&currentchar<='4') {
								baoshi |=1<<(currentchar-'0');
						}
						if (visit[nx][ny][baoshi]==1)continue;
						visit[nx][ny][baoshi]=1;
						
						 //2、如果为传送门$,
						if (currentchar=='$') {
							for(int j=0;j<countchuan;j++) {
								nx=chuanqueue[j].getNx();
								ny=chuanqueue[j].getNy();
								if(visit[nx][ny][baoshi]==1)continue;
								visit[nx][ny][baoshi]=1;
								queue.add(new step(nx, ny, (current.getPre()+1),baoshi));
							}
						}
						
						//3、如果是'.'
						queue.add(new step(nx, ny, (current.getPre()+1),baoshi));
				}
			}
			if(!flag)System.out.println("oop!");
			T--;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值