不会真的有人是来看题的吧。代码的话dfs和bfs也没有什么很复杂的思路,慢慢推就行,只是需要磕磕碰碰调一些细节,但不存在像动态规划写不出来的状态。
所以这篇博客不讲技术讲我的思路还有吐槽。
首先从一个点去到另一个点这个事件可以构成一棵决策树,是一棵树那么就有点最优子结构的意思了,如下面这个迷宫:
010000
000100
001001
110000
f(2,1)=MIN( f(3,1) , f(2,2) ) +1
然后好像也有重复的子问题,比如f(3,1) 和 f(2,2) 都和 f(3,2)有关。然后只要再满足无后效性就可以用动态规划来做了,让我们复习一下使用动态规划需要满足的3个条件:
1.最优子结构
2.重复子问题
3.无后效性
什么是无后效性呢?记得书上说的是——当前的决策只和当前的状态有关,过去的决策不会影响现在的决策,每个状态都是对历史决策的一个完整总结。
我的理解,就是为了让你过去的决策不会限制到子问题的决策,这样子问题的所有可能的决策是相同的,它的最优解也就是相同的,就实现了两个子问题是真正重复可以复用的了。
可上面那个迷宫,当你走到了(3,1)求它的子问题f(3,2)时,子问题f(3,2)的所有可能决策是向上走;而当你走到了(2,2)求它的子问题f(3,2)时,子问题f(3,2)的所有可能决策是向左走。可能情况不同,最终结果也不一定相同,所以不能使用动态规划通过填表法来复用子问题。
然后我对于遍历的题一直是用dfs的,对于bfs从来没有编码实现过,没想到这题用dfs运行时间会那么长,也是在准备蓝桥杯的时候我才第一次体会到一个问题运行几十秒都不出结果,搞得我还以为是程序有死循环了。后面发现这题用dfs就是很慢。
先保存一下我的dfs代码:
import java.util.Scanner;
public class Main {
static int minest=1600;
static String path;
static int[][] direction={{1,0},{0,-1},{0,1},{-1,0}};
static String[] move={"D","L","R","U"};
public static void main(String[] args) {
int[][] puzzle=new int[31][51];
Scanner scanner=new Scanner(System.in);
for (int i = 1; i <= 30; i++) {
String line=scanner.nextLine();
for (int j = 1; j <= 50; j++) {
puzzle[i][j]=line.charAt(j-1)-'0';
}
}
dfs(puzzle,1,1,0,"");
System.out.println(path);
}
public static void dfs(int[][] puzzle,int x,int y,int times,String track) {
puzzle[x][y]=1;
System.out.println(x+","+y);
if (times>=minest){puzzle[x][y]=0;return;}
if (x==30&&y==50){
minest=times;
path=track;
}
for (int i = 0; i < 4; i++) {
int xx=x+direction[i][0];
int yy=y+direction[i][1];
if (xx>0&&xx<31&&yy>0&&yy<51&&puzzle[xx][yy]==0){dfs(puzzle,xx,yy,times+1,track+move[i]);}
}
puzzle[x][y]=0;
}
}
哎想证明一下在哪个临界点bfs比dfs要快一些的想法是失败了,哪怕是没有障碍物的每个节点4种选择情况的决策树也不好分析。反正这题用dfs跑不出来,我不管下次还用dfs,if(真实比赛&&证明了我的dfs代码没有死循环,即用一个小一点的测试用例可以跑出来结果&&做完会做的题还有时间)我再用bfs来写一遍吧;
在知道了只能用bfs的情况下,下面是我的bfs代码:
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Scanner;
public class Main {
static Queue<Point> stack=new ArrayDeque<Point>() ;
static int minest=1600;
static String path;
static int[][] direction={{1,0},{0,-1},{0,1},{-1,0}};
static String[] move={"D","L","R","U"};
public static void main(String[] args) {
int[][] puzzle=new int[31][51];
Point temp;
int index=0;
Scanner scanner=new Scanner(System.in);
for (int i = 1; i <= 30; i++) {
String line=scanner.nextLine();
for (int j = 1; j <= 50; j++) {
puzzle[i][j]=line.charAt(j-1)-'0';
}
}
puzzle[1][1]=1;
stack.add(new Point(1,1,0,""));
while (!stack.isEmpty()){
temp=stack.remove();
if (++index<=20) System.out.println(temp);
if (temp.x==30&&temp.y==50){minest=temp.times;path=temp.path;break;}
for (int i = 0; i < 4; i++) {
int xx=temp.x+direction[i][0];
int yy=temp.y+direction[i][1];
if (xx>0&&xx<31&&yy>0&&yy<51&&puzzle[xx][yy]==0){puzzle[xx][yy]=1;stack.add(new Point(xx,yy,temp.times+1,temp.path+move[i]));}
}
}
System.out.println(minest);
System.out.println(path);
}
static class Point{
int x;
int y;
int times;
String path;
public Point(int x, int y, int times, String path) {
this.x = x;
this.y = y;
this.times = times;
this.path = path;
}
}
}
小插曲是这里的数据结构我一开始是栈,然后代码逻辑是对的,居然跑出来一个286,当时就很觉得自己很菜,想去学习别人的bfs才发现怎么queue这个单词我没使用过,把栈改回队列就好了。
我也不是那么菜吗,继续冲冲冲!