恭喜博主自己花了两天时间终于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&¤t.getY()==gongzhuY&¤t.getK()>=(1<<k)-1) {
System.out.println(current.getPre());
flag=true;
break;
}else if(current.getX()==gongzhuX&¤t.getY()==gongzhuY&¤t.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'&¤tchar<='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--;
}
}
}