每天10:24,干货准时送达!
小史是一个应届生,虽然学的是电子专业,但是自己业余时间看了很多互联网与编程方面的书,一心想进BAT互联网公司。
今天小史又去了一家互联网小巨头公司面试了。
【面试现场】
小史眉头一皱,发现事情并不简单。
题目:在数字矩阵中,从左上角到右下角,每次只能向右或向下,如何规划路径,能获得最大数字总和?
小史开始仔细分析问题,一时间竟想不到很好的方法。
小史心中反复默念题目,进行思考。
小史仔细回忆起了吕老师教他的华容道搜索算法。
【请教大神】
回到学校,小史把面试情况和吕老师说了一下。
吕老师:红色和蓝色两条路都能到达中间的100这个点,但是很明显,红色的路拿到的奖金更多。所以蓝色的路,后面不管再怎么走,都不可能拿到最大的奖金数了。
吕老师:假设蓝色路径再往后走出一条绿色路径是最大奖金,那么我把蓝色路径替换成红色路径,红色加绿色得到的奖金就比蓝色加绿色得到的多呀。
【记忆化搜索】
小史:哦,我明白了,这样我每搜到一个点,都可以和这个数比较,如果比它大,就更新这个数,继续搜,如果比它小,就不搜了,直接剪枝。
理解了算法之后,小史的代码写起来也是非常快,不一会儿就写好了:
DeepFirstSearch.java
import java.util.ArrayList;import java.util.List;/** * @author xiaoshi on 2018/10/20. */public class DeepFirstSearch { // 定义best(i,j) 和 M N private int[][] best = null; private int M = 0; private int N = 0; // 定义方向常量 private static final int RIGHT = 1; private static final int DOWN = 2; // 当前搜索的方向数组 private List<Integer> curPath = null; // 记录最大值对应的方向数组 private Integer[] bestPath = null; // 当前搜索点 private int curX = 0; private int curY = 0; // 当前搜索累计值 private int value = 0; // 记录搜索到的最大值 private int maxValue = 0; // 往某个方向前进 private void goDir(int dir, int[][] matrix) { // 前进 if(dir == DOWN) { curX++; value += matrix[curX][curY]; } else if(dir == RIGHT) { curY++; value += matrix[curX][curY]; } // 记录方向 curPath.add(dir); } // 往某个方向回退 private void goBackDir(int dir, int[][] matrix) { // 回退 if(dir == DOWN) { value -= matrix[curX][curY]; curX--; } else if(dir == RIGHT) { value -= matrix[curX][curY]; curY--; } // 移除方向 curPath.remove(curPath.size() - 1); } // 深搜 private void search(int dir, int[][] matrix) { // 往某方向前进 goDir(dir, matrix); // 到达终点 if(curX == M - 1 && curY == N - 1) { if(value > maxValue) { // 记录最大值和路径 maxValue = value; bestPath = new Integer[curPath.size()]; curPath.toArray(bestPath); } } else if(value <= best[curX][curY]) { // 不搜索了,等着goBack,剪枝 } else { // 更新best(i,j),记忆 best[curX][curY] = value; // 朝下一个方向搜索 if(curX < M - 1) { search(DOWN, matrix); } if(curY < N - 1) { search(RIGHT, matrix); } } // 往某方向回退 goBackDir(dir, matrix); } // 获取最大值 public int getMaxAward(int[][] matrix) { // 初始化 value = matrix[0][0]; M = matrix.length; N = matrix[0].length; best = new int[M][N]; curPath = new ArrayList<Integer>(); // 开始搜索 if(M > 1) { search(DOWN, matrix); } if(N > 1) { search(RIGHT, matrix); } // 最大值 return maxValue; } // 打印最佳路径 public void printBestPath() { // 打印路径 for(int i = 0; i < bestPath.length; i++) { if(bestPath[i] == RIGHT) { System.out.print("右 "); } else { System.out.print("下 "); } } System.out.println(); }}
/**
* @author xiaoshi on 2018/10/20.
*/
public class DeepFirstSearch {
// 定义best(i,j) 和 M N
private int[][] best = null;
private int M = 0;
private int N = 0;
// 定义方向常量
private static final int RIGHT = 1;
private static final int DOWN = 2;
// 当前搜索的方向数组
private List<Integer> curPath = null;
// 记录最大值对应的方向数组
private Integer[] bestPath = null;
// 当前搜索点
private int curX = 0;
private int curY = 0;
// 当前搜索累计值
private int value = 0;
// 记录搜索到的最大值
private int maxValue = 0;
// 往某个方向前进
private void goDir(int dir, int[][] matrix) {
// 前进
if(dir == DOWN) {
curX++;
value += matrix[curX][curY];
} else if(dir == RIGHT) {
curY++;
value += matrix[curX][curY];
}
// 记录方向
curPath.add(dir);
}
// 往某个方向回退
private void goBackDir(int dir, int[][] matrix) {
// 回退
if(dir == DOWN) {
value -= matrix[curX][curY];
curX--;
} else if(dir == RIGHT) {
value -= matrix[curX][curY];
curY--;
}
// 移除方向
curPath.remove(curPath.size() - 1);
}
// 深搜
private void search(int dir, int[][] matrix) {
// 往某方向前进
goDir(dir, matrix);
// 到达终点
if(curX == M - 1 && curY == N - 1) {
if(value > maxValue) {
// 记录最大值和路径
maxValue = value;
bestPath = new Integer[curPath.size()];
curPath.toArray(bestPath);
}
} else if(value <= best[curX][curY]) {
// 不搜索了,等着goBack,剪枝
} else {
// 更新best(i,j),记忆
best[curX][curY] = value;
// 朝下一个方向搜索
if(curX < M - 1) {
search(DOWN, matrix);
}
if(curY < N - 1) {
search(RIGHT, matrix);
}
}
// 往某方向回退
goBackDir(dir, matrix);
}
// 获取最大值
public int getMaxAward(int[][] matrix) {
// 初始化
value = matrix[0][0];
M = matrix.length;
N = matrix[0].length;
best = new int[M][N];
curPath = new ArrayList<Integer>();
// 开始搜索
if(M > 1) {
search(DOWN, matrix);
}
if(N > 1) {
search(RIGHT, matrix);
}
// 最大值
return maxValue;
}
// 打印最佳路径
public void printBestPath() {
// 打印路径
for(int i = 0; i < bestPath.length; i++) {
if(bestPath[i] == RIGHT) {
System.out.print("右 ");
} else {
System.out.print("下 ");
}
}
System.out.println();
}
}
(友情提示:可左右滑动)
Main.java
/** * @author xiaoshi on 2018/10/20. */public class Main { public static void main(String[] args) { int[][] matrix1 = { {300, 500, 560, 400, 160}, {1000, 100, 200, 340, 690}, {600, 500, 500, 460, 320}, {300, 400, 250, 210, 760} }; int[][] matrix2 = { {300, 500, 2560, 400}, {1000, 100, 200, 340}, {600, 500, 500, 460}, {300, 400, 250, 210}, {860, 690, 320, 760} }; DeepFirstSearch dp = new DeepFirstSearch(); System.out.println(dp.getMaxAward(matrix1)); dp.printBestPath(); System.out.println(dp.getMaxAward(matrix2)); dp.printBestPath(); }}
public class Main {
public static void main(String[] args) {
int[][] matrix1 = {
{300, 500, 560, 400, 160},
{1000, 100, 200, 340, 690},
{600, 500, 500, 460, 320},
{300, 400, 250, 210, 760}
};
int[][] matrix2 = {
{300, 500, 2560, 400},
{1000, 100, 200, 340},
{600, 500, 500, 460},
{300, 400, 250, 210},
{860, 690, 320, 760}
};
DeepFirstSearch dp = new DeepFirstSearch();
System.out.println(dp.getMaxAward(matrix1));
dp.printBestPath();
System.out.println(dp.getMaxAward(matrix2));
dp.printBestPath();
}
}
(友情提示:可左右滑动)
运行结果
4440下 下 右 右 右 右 下 5530右 右 右 下 下 下 下
5530
右 右 右 下 下 下 下
【记忆广搜】
吕老师:记忆深搜确实可以剪枝,但是假如有人刻意安排数字,把较小的数都安排在你先搜的路径上,那么你的计算量还是不会减少太多。
小史:还有这么坏的人呢?不过你这样一说我到想起来,深搜确实缺少一种“全局观”,可能第一条路搜完了,再来看第二条路,发现更好,结果第一条路全白搜了。
理解了算法之后,小史的代码写起来也是非常快,不一会儿就写好了:
BreadthFirstSearch.java
import java.util.ArrayList;import java.util.LinkedList;import java.util.List;/** * @author xiaoshi on 2018/10/20. */public class BreadthFirstSearch { // 定义 M N private int M = 0; private int N = 0; // 定义方向常量 private static final int RIGHT = 1; private static final int DOWN = 2; // 代表广搜的每一步,通过lastItem链起来 private class SearchItem { private int curX; private int curY; private int dir; // 这里记录 (curX, curY) 路径的最大值,相当于best(i,j) private int value; private SearchItem lastItem; public SearchItem(int curX, int curY, int dir, int value) { this.curX = curX; this.curY = curY; this.dir = dir; this.value = value; } } // 最终搜到的结果 private SearchItem bestItem = null; // 广搜的存储队列 private List<SearchItem> statusToSearch = new LinkedList<SearchItem>(); // 替换广搜队列中较小的item,返回值为搜索队列中是否找到相应item private boolean replaceStatusList(SearchItem newItem) { // 是否找到 boolean find = false; // 遍历查找 for(int i=0; i<statusToSearch.size(); i++) { SearchItem searchItem = statusToSearch.get(i); // 找到对应item if(searchItem.curX == newItem.curX && searchItem.curY == searchItem.curY) { find = true; // 这里相当于在对比best(i,j) if(searchItem.value < newItem.value) { // 替换成更好的item statusToSearch.remove(i); statusToSearch.add(i, newItem); } break; } } return find; } // 广搜 private void search(int[][] matrix) { // 搜索队列中不为空 while(statusToSearch.size() > 0) { // 从搜索队列中获取一个item SearchItem searchItem = statusToSearch.remove(0); int curX = searchItem.curX; int curY = searchItem.curY; int curValue = searchItem.value; // 如果已经搜到 if(curX == M - 1 && curY == N - 1) { bestItem = searchItem; } // 可往下搜 if(curX < M - 1) { SearchItem newItem = new SearchItem(curX + 1, curY, DOWN, curValue + matrix[curX + 1][curY]); newItem.lastItem = searchItem; // 是否替代 if(!replaceStatusList(newItem)) { // 搜索队列中没有找到,则添加 statusToSearch.add(newItem); } } // 可往右搜 if(curY < N - 1) { SearchItem newItem = new SearchItem(curX, curY + 1, RIGHT, curValue + matrix[curX][curY + 1]); newItem.lastItem = searchItem; // 是否替代 if(!replaceStatusList(newItem)) { // 搜索队列中没有找到,则添加 statusToSearch.add(newItem); } } } } // 获取最大值 public int getMaxAward(int[][] matrix) { // 初始化 M = matrix.length; N = matrix[0].length; int value = matrix[0][0]; SearchItem searchItem = new SearchItem(0, 0, 0, value); statusToSearch.add(searchItem); // 开始搜索 search(matrix); // 返回最大值 return bestItem.value; } // 打印最佳路径 public void printBestPath() { List<Integer> dirList = new ArrayList<Integer>(); SearchItem curSearchItem = bestItem; // 根据lastItem,找到路径 while(null != curSearchItem) { int dir = curSearchItem.dir; if(dir == DOWN) { dirList.add(DOWN); } else if(dir == RIGHT) { dirList.add(RIGHT); } curSearchItem = curSearchItem.lastItem; } // 打印路径 for(int i = dirList.size() - 1; i >= 0; i--) { if(dirList.get(i) == DOWN) { System.out.print("下 "); } else if(dirList.get(i) == RIGHT) { System.out.print("右 "); } } System.out.println(); }}
import java.util.List;
/**
* @author xiaoshi on 2018/10/20.
*/
public class BreadthFirstSearch {
// 定义 M N
private int M = 0;
private int N = 0;
// 定义方向常量
private static final int RIGHT = 1;
private static final int DOWN = 2;
// 代表广搜的每一步,通过lastItem链起来
private class SearchItem {
private int curX;
private int curY;
private int dir;
// 这里记录 (curX, curY) 路径的最大值,相当于best(i,j)
private int value;
private SearchItem lastItem;
public SearchItem(int curX, int curY, int dir, int value) {
this.curX = curX;
this.curY = curY;
this.dir = dir;
this.value = value;
}
}
// 最终搜到的结果
private SearchItem bestItem = null;
// 广搜的存储队列
private List<SearchItem> statusToSearch = new LinkedList<SearchItem>();
// 替换广搜队列中较小的item,返回值为搜索队列中是否找到相应item
private boolean replaceStatusList(SearchItem newItem) {
// 是否找到
boolean find = false;
// 遍历查找
for(int i=0; i<statusToSearch.size(); i++) {
SearchItem searchItem = statusToSearch.get(i);
// 找到对应item
if(searchItem.curX == newItem.curX && searchItem.curY == searchItem.curY) {
find = true;
// 这里相当于在对比best(i,j)
if(searchItem.value < newItem.value) {
// 替换成更好的item
statusToSearch.remove(i);
statusToSearch.add(i, newItem);
}
break;
}
}
return find;
}
// 广搜
private void search(int[][] matrix) {
// 搜索队列中不为空
while(statusToSearch.size() > 0) {
// 从搜索队列中获取一个item
SearchItem searchItem = statusToSearch.remove(0);
int curX = searchItem.curX;
int curY = searchItem.curY;
int curValue = searchItem.value;
// 如果已经搜到
if(curX == M - 1 && curY == N - 1) {
bestItem = searchItem;
}
// 可往下搜
if(curX < M - 1) {
SearchItem newItem = new SearchItem(curX + 1, curY, DOWN, curValue + matrix[curX + 1][curY]);
newItem.lastItem = searchItem;
// 是否替代
if(!replaceStatusList(newItem)) {
// 搜索队列中没有找到,则添加
statusToSearch.add(newItem);
}
}
// 可往右搜
if(curY < N - 1) {
SearchItem newItem = new SearchItem(curX, curY + 1, RIGHT, curValue + matrix[curX][curY + 1]);
newItem.lastItem = searchItem;
// 是否替代
if(!replaceStatusList(newItem)) {
// 搜索队列中没有找到,则添加
statusToSearch.add(newItem);
}
}
}
}
// 获取最大值
public int getMaxAward(int[][] matrix) {
// 初始化
M = matrix.length;
N = matrix[0].length;
int value = matrix[0][0];
SearchItem searchItem = new SearchItem(0, 0, 0, value);
statusToSearch.add(searchItem);
// 开始搜索
search(matrix);
// 返回最大值
return bestItem.value;
}
// 打印最佳路径
public void printBestPath() {
List<Integer> dirList = new ArrayList<Integer>();
SearchItem curSearchItem = bestItem;
// 根据lastItem,找到路径
while(null != curSearchItem) {
int dir = curSearchItem.dir;
if(dir == DOWN) {
dirList.add(DOWN);
} else if(dir == RIGHT) {
dirList.add(RIGHT);
}
curSearchItem = curSearchItem.lastItem;
}
// 打印路径
for(int i = dirList.size() - 1; i >= 0; i--) {
if(dirList.get(i) == DOWN) {
System.out.print("下 ");
} else if(dirList.get(i) == RIGHT) {
System.out.print("右 ");
}
}
System.out.println();
}
}
(友情提示:可左右滑动)
Main.java
/** * @author xiaoshi on 2018/10/20. */public class Main { public static void main(String[] args) { int[][] matrix1 = { {300, 500, 560, 400, 160}, {1000, 100, 200, 340, 690}, {600, 500, 500, 460, 320}, {300, 400, 250, 210, 760} }; int[][] matrix2 = { {300, 500, 2560, 400}, {1000, 100, 200, 340}, {600, 500, 500, 460}, {300, 400, 250, 210}, {860, 690, 320, 760} }; BreadthFirstSearch dp = new BreadthFirstSearch(); System.out.println(dp.getMaxAward(matrix1)); dp.printBestPath(); System.out.println(dp.getMaxAward(matrix2)); dp.printBestPath(); }}
public class Main {
public static void main(String[] args) {
int[][] matrix1 = {
{300, 500, 560, 400, 160},
{1000, 100, 200, 340, 690},
{600, 500, 500, 460, 320},
{300, 400, 250, 210, 760}
};
int[][] matrix2 = {
{300, 500, 2560, 400},
{1000, 100, 200, 340},
{600, 500, 500, 460},
{300, 400, 250, 210},
{860, 690, 320, 760}
};
BreadthFirstSearch dp = new BreadthFirstSearch();
System.out.println(dp.getMaxAward(matrix1));
dp.printBestPath();
System.out.println(dp.getMaxAward(matrix2));
dp.printBestPath();
}
}
(友情提示:可左右滑动)
运行结果
4440下 下 右 右 右 右 下 5530右 右 右 下 下 下 下
5530
右 右 右 下 下 下 下
【动态规划】
吕老师:小史,代码写得不错,咱们再来看看广搜的过程,其实就是在搜索的过程中从左上到右下计算出了best(i,j),所以为啥我们不能直接算出best(i,j)呢?
理解了算法之后,小史的代码写起来也是非常快,不一会儿就写好了:
DynamicProgramming.java
import java.util.ArrayList;import java.util.List;/** * @author xiaoshi on 2018/10/20. */public class DynamicProgramming { // 定义best(i,j) 和 M N private int[][] best = null; private int M = 0; private int N = 0; // 定义方向常量 private static final int RIGHT = 1; private static final int DOWN = 2; // 存储最好路径 private List<Integer> bestPath = null; // 计算best(i,j) private void calcDp(int[][] matrix) { // 初始化 M = matrix.length; N = matrix[0].length; best = new int[M][N]; // 计算 for(int i = 0; i < M; i++) { for(int j = 0; j < N; j++) { // 边界 if(i == 0 && j == 0) { best[i][j] = matrix[i][j]; } else if(i == 0) { best[i][j] = best[i][j - 1] + matrix[i][j]; } else if(j == 0) { best[i][j] = best[i - 1][j] + matrix[i][j]; } else { // 状态转移 best[i][j] = Math.max(best[i - 1][j], best[i][j - 1]) + matrix[i][j]; } } } } // 获取最大值 public int getMaxAward(int[][] matrix) { // 计算状态 calcDp(matrix); // 计算最佳路径 calcBestPath(); // 返回最大值 return best[matrix.length - 1][matrix[0].length - 1]; } // 计算最佳路径,从后往前 private void calcBestPath() { bestPath = new ArrayList<Integer>(); // 总共走 M + N - 2 步 int curX = M - 1; int curY = N - 1; // 根据best(i,j)计算最佳路径 for(int i = 0; i < M + N - 2; i++) { if(curX == 0) { curY--; bestPath.add(RIGHT); } else if(curY == 0) { curX--; bestPath.add(DOWN); } else { if(best[curX - 1][curY] > best[curX][curY - 1]) { curX--; bestPath.add(DOWN); } else { curY--; bestPath.add(RIGHT); } } } } // 打印最佳路径 public void printBestPath() { // 倒序打印 for(int i = bestPath.size() - 1; i >= 0; i--) { if(bestPath.get(i) == RIGHT) { System.out.print("右 "); } else { System.out.print("下 "); } } System.out.println(); }}
/**
* @author xiaoshi on 2018/10/20.
*/
public class DynamicProgramming {
// 定义best(i,j) 和 M N
private int[][] best = null;
private int M = 0;
private int N = 0;
// 定义方向常量
private static final int RIGHT = 1;
private static final int DOWN = 2;
// 存储最好路径
private List<Integer> bestPath = null;
// 计算best(i,j)
private void calcDp(int[][] matrix) {
// 初始化
M = matrix.length;
N = matrix[0].length;
best = new int[M][N];
// 计算
for(int i = 0; i < M; i++) {
for(int j = 0; j < N; j++) {
// 边界
if(i == 0 && j == 0) {
best[i][j] = matrix[i][j];
} else if(i == 0) {
best[i][j] = best[i][j - 1] + matrix[i][j];
} else if(j == 0) {
best[i][j] = best[i - 1][j] + matrix[i][j];
} else {
// 状态转移
best[i][j] = Math.max(best[i - 1][j], best[i][j - 1]) + matrix[i][j];
}
}
}
}
// 获取最大值
public int getMaxAward(int[][] matrix) {
// 计算状态
calcDp(matrix);
// 计算最佳路径
calcBestPath();
// 返回最大值
return best[matrix.length - 1][matrix[0].length - 1];
}
// 计算最佳路径,从后往前
private void calcBestPath() {
bestPath = new ArrayList<Integer>();
// 总共走 M + N - 2 步
int curX = M - 1;
int curY = N - 1;
// 根据best(i,j)计算最佳路径
for(int i = 0; i < M + N - 2; i++) {
if(curX == 0) {
curY--;
bestPath.add(RIGHT);
} else if(curY == 0) {
curX--;
bestPath.add(DOWN);
} else {
if(best[curX - 1][curY] > best[curX][curY - 1]) {
curX--;
bestPath.add(DOWN);
} else {
curY--;
bestPath.add(RIGHT);
}
}
}
}
// 打印最佳路径
public void printBestPath() {
// 倒序打印
for(int i = bestPath.size() - 1; i >= 0; i--) {
if(bestPath.get(i) == RIGHT) {
System.out.print("右 ");
} else {
System.out.print("下 ");
}
}
System.out.println();
}
}
(友情提示:可左右滑动)
Main.java
/** * @author xiaoshi on 2018/10/20. */public class Main { public static void main(String[] args) { int[][] matrix1 = { {300, 500, 560, 400, 160}, {1000, 100, 200, 340, 690}, {600, 500, 500, 460, 320}, {300, 400, 250, 210, 760} }; int[][] matrix2 = { {300, 500, 2560, 400}, {1000, 100, 200, 340}, {600, 500, 500, 460}, {300, 400, 250, 210}, {860, 690, 320, 760} }; DynamicProgramming dp = new DynamicProgramming(); System.out.println(dp.getMaxAward(matrix1)); dp.printBestPath(); System.out.println(dp.getMaxAward(matrix2)); dp.printBestPath(); }}
public class Main {
public static void main(String[] args) {
int[][] matrix1 = {
{300, 500, 560, 400, 160},
{1000, 100, 200, 340, 690},
{600, 500, 500, 460, 320},
{300, 400, 250, 210, 760}
};
int[][] matrix2 = {
{300, 500, 2560, 400},
{1000, 100, 200, 340},
{600, 500, 500, 460},
{300, 400, 250, 210},
{860, 690, 320, 760}
};
DynamicProgramming dp = new DynamicProgramming();
System.out.println(dp.getMaxAward(matrix1));
dp.printBestPath();
System.out.println(dp.getMaxAward(matrix2));
dp.printBestPath();
}
}
(友情提示:可左右滑动)
运行结果
4440下 下 右 右 右 右 下 5530右 右 右 下 下 下 下
5530
右 右 右 下 下 下 下
【动态规划解析】
吕老师:状态的定义要满足几个点,第一,问题的答案是某种状态,或者可由状态进行推导。第二,当前状态可以由之前的状态推导而来。
【状态压缩】
小史:哦,我知道了,这道题,如果按照斜线方向来计算,只需要保留一条斜线的状态,就能计算出下一条斜线。所以之前的状态就不需要保留了。
理解了算法之后,小史的代码写起来也是非常快,不一会儿就写好了:
DpCompressed.java
/** * @author xiaoshi on 2018/10/20. */public class DpCompressed { // 定义best(i) 和 M N private int[][] best = null; private int M = 0; private int N = 0; // 计算best(i) private void calcDp(int[][] matrix) { // 初始化 M = matrix.length; N = matrix[0].length; int minMN = Math.min(M, N); int maxMN = Math.max(M, N); // best只需要M和N的最小值长度就行 best = new int[2][minMN]; // 需要计算 M+N-1 条斜线 for(int i = 0; i < M + N - 1; i++) { // 第 i 条斜线的起始x坐标 int startX = 0; // 第 i 条斜线的起始y坐标 int startY = i; // 第 i 条斜线的数字个数 int number = i + 1; if(i >= N) { startX = i + 1 - N; startY = N - 1; } if(i >= minMN) { number = minMN; } if(i >= maxMN) { number = M + N - i - 1; } // 第 i 条斜线上的 number 个数 for(int j = 0; j < number; j++) { // 起始点 if(i == 0 && j == 0) { best[1][j] = matrix[startX + j][startY - j]; } else { if (i < N) { // 前半部分 if (j == 0) { // 上边界 best[1][j] = best[0][j] + matrix[startX + j][startY - j]; } else if (j == number - 1) { // 左边界 best[1][j] = best[0][j-1] + matrix[startX + j][startY - j]; } else { // 状态转移 best[1][j] = Math.max(best[0][j], best[0][j-1]) + matrix[startX + j][startY - j]; } } else { // 后半部分 if(i < M && j == number - 1) { // 左边界 best[1][j] = best[0][j] + matrix[startX + j][startY - j]; } else { // 状态转移 best[1][j] = Math.max(best[0][j], best[0][j+1]) + matrix[startX + j][startY - j]; } } } } // 将best[1]上的状态拷贝到best[0] for(int j = 0; j < number; j++) { best[0][j] = best[1][j]; } } } // 获取最大值 public int getMaxAward(int[][] matrix) { calcDp(matrix); return best[0][0]; }}
public class DpCompressed {
// 定义best(i) 和 M N
private int[][] best = null;
private int M = 0;
private int N = 0;
// 计算best(i)
private void calcDp(int[][] matrix) {
// 初始化
M = matrix.length;
N = matrix[0].length;
int minMN = Math.min(M, N);
int maxMN = Math.max(M, N);
// best只需要M和N的最小值长度就行
best = new int[2][minMN];
// 需要计算 M+N-1 条斜线
for(int i = 0; i < M + N - 1; i++) {
// 第 i 条斜线的起始x坐标
int startX = 0;
// 第 i 条斜线的起始y坐标
int startY = i;
// 第 i 条斜线的数字个数
int number = i + 1;
if(i >= N) {
startX = i + 1 - N;
startY = N - 1;
}
if(i >= minMN) {
number = minMN;
}
if(i >= maxMN) {
number = M + N - i - 1;
}
// 第 i 条斜线上的 number 个数
for(int j = 0; j < number; j++) {
// 起始点
if(i == 0 && j == 0) {
best[1][j] = matrix[startX + j][startY - j];
} else {
if (i < N) {
// 前半部分
if (j == 0) {
// 上边界
best[1][j] = best[0][j] + matrix[startX + j][startY - j];
} else if (j == number - 1) {
// 左边界
best[1][j] = best[0][j-1] + matrix[startX + j][startY - j];
} else {
// 状态转移
best[1][j] = Math.max(best[0][j], best[0][j-1]) + matrix[startX + j][startY - j];
}
} else {
// 后半部分
if(i < M && j == number - 1) {
// 左边界
best[1][j] = best[0][j] + matrix[startX + j][startY - j];
} else {
// 状态转移
best[1][j] = Math.max(best[0][j], best[0][j+1]) + matrix[startX + j][startY - j];
}
}
}
}
// 将best[1]上的状态拷贝到best[0]
for(int j = 0; j < number; j++) {
best[0][j] = best[1][j];
}
}
}
// 获取最大值
public int getMaxAward(int[][] matrix) {
calcDp(matrix);
return best[0][0];
}
}
(友情提示:可左右滑动)
Main.java
/** * @author xiaoshi on 2018/10/20. */public class Main { public static void main(String[] args) { int[][] matrix1 = { {300, 500, 560, 400, 160}, {1000, 100, 200, 340, 690}, {600, 500, 500, 460, 320}, {300, 400, 250, 210, 760} }; int[][] matrix2 = { {300, 500, 2560, 400}, {1000, 100, 200, 340}, {600, 500, 500, 460}, {300, 400, 250, 210}, {860, 690, 320, 760} }; DpCompressed dp = new DpCompressed(); System.out.println(dp.getMaxAward(matrix1)); System.out.println(dp.getMaxAward(matrix2)); }}
public class Main {
public static void main(String[] args) {
int[][] matrix1 = {
{300, 500, 560, 400, 160},
{1000, 100, 200, 340, 690},
{600, 500, 500, 460, 320},
{300, 400, 250, 210, 760}
};
int[][] matrix2 = {
{300, 500, 2560, 400},
{1000, 100, 200, 340},
{600, 500, 500, 460},
{300, 400, 250, 210},
{860, 690, 320, 760}
};
DpCompressed dp = new DpCompressed();
System.out.println(dp.getMaxAward(matrix1));
System.out.println(dp.getMaxAward(matrix2));
}
}
(友情提示:可左右滑动)
运行结果
44405530
PS:这次这篇花费了两周时间。如果你看到了这里,并且有所收获的话,可以动动手指转发一下哦,小史和吕老师都会感谢你。
面试现场是公众号【互联网侦察】推出的一个全新的板块,感兴趣的小伙伴可以直接搜索公众号名字关注哦!面试现场系列旨在回放真实的面试过程,并对面试题进行全面解析,提供多种思路,比较优劣,希望对大家的面试有所帮助。
![640?](https://i-blog.csdnimg.cn/blog_migrate/c04bb4e8f5351efdcf579ec572d05165.png)
【面试现场】如何在500w个单词中统计特定前缀的单词有多少个?
喜欢本文的朋友们,欢迎关注订阅号Java学习之道,收看更多精彩内容!
一 起努力吧!