109. Triangle:点击打开链接
方法一:自底向上
例如:从6开始,依赖于4和1中小者,再加上本身
注意:这种三角形第n行的长度是n+1,一共有n-1行,第0行的长度为1,
Time:O(n^2),而如果用DFS则需O(2^n),因此可知DP优化了很多
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
[
[2],
[3,4],
[7,6,10], 遍历完最后一行,这一行成最后一行,相当于数组最后一行该加的数加上去后被弃掉,不断用本行最小的向上垒
[4, 1,8,3]
class Solution {
public int rob(int[] nums) {
if(nums == null || nums.length == 0)
{
return 0;
}
if(nums.length == 1)
{
return nums[0];
}
int[] dp = new int[nums.length];
dp[0] = nums[0];
dp[1]= Math.max(nums[0], nums[1]);
for(int i=2; i<nums.length; i++)
{
dp[i] = Math.max(nums[i] + dp[i-2], dp[i-1]);
}
return dp[nums.length - 1];
}
}
public class Solution {
/**
* @param triangle: a list of lists of integers.
* @return: An integer, minimum path sum.
*/
public int minimumTotal(int[][] triangle) {
if(triangle==null || triangle.length==0){
return 0;
}
if(triangle[0]==null || triangle[0].length==0){
return 0;
}
int n=triangle.length;
int[][] f=new int[n][n];
for(int i=0;i<n;i++){ //因为是自底向上,初始化底部的每一个值
f[n-1][i]=triangle[n-1][i];
}
for(int i=n-2;i>=0;i--){
for(int j=0;j<=i;j++){
f[i][j]=Math.min(f[i+1][j],f[i+1][j+1])+triangle[i][j];
}
}
return f[0][0];
}
}
public class Solution {
/**
* @param triangle: a list of lists of integers.
* @return: An integer, minimum path sum.
*/
public int minimumTotal(int[][] triangle) { //和上面是一样的,只是写法上更容易理解
if(triangle==null || triangle.length==0){
return 0;
}
if(triangle[0]==null || triangle[0].length==0){
return 0;
}
int n=triangle.length;
for(int i=n-2;i>=0;i--){
for(int j=0;j<=i;j++){
triangle[i][j]=Math.min(triangle[i+1][j],triangle[i+1][j+1])+triangle[i][j];
}
}
return triangle[0][0];
}
}
方法二:自顶向下
[ [2],
[3,4],
[6,5,7],
[4,1,8,3] 要初始化的值
]
public class Solution {
/**
* @param triangle: a list of lists of integers.
* @return: An integer, minimum path sum.
*/
public int minimumTotal(int[][] triangle) {
if(triangle==null || triangle.length==0){
return 0;
}
if(triangle[0]==null || triangle[0].length==0){
return 0;
}
int n=triangle.length;
int[][] f=new int[n][n];
f[0][0]=triangle[0][0];
for(int i=1;i<n;i++){ //要初始化每一行第一个值和最后一个值
f[i][0]=f[i-1][0]+triangle[i][0];
f[i][i]=f[i-1][i-1]+triangle[i][i];
}
for(int i=1;i<n;i++){ //除了每一行第一个值和最后一个值
for(int j=1;j<i;j++){
f[i][j]=Math.min(f[i-1][j-1],f[i-1][j])+triangle[i][j];
}
}
int minVal=f[n-1][0]; //最后就是在f[][]的最后一行里找最小值
for(int i=1;i<n;i++){
minVal=Math.min(minVal,f[n-1][i]);
}
return minVal;
}
}
LeetCode版本:点击打开链接
注意:set( )方法的使用
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle==null || triangle.size()==0){
return 0;
}
int n=triangle.size();
for(int i=n-2;i>=0;i--){
for(int j=0;j<=i;j++){
triangle.get(i).set(j,Math.min(triangle.get(i+1).get(j),triangle.get(i+1).get(j+1))+triangle.get(i).get(j));
} //这里的set方法,把j参数位置set成后面的内容
}
return triangle.get(0).get(0);
}
}
110. Minimum Path Sum: 点击打开链接
(0,0) | (0,1) | (0,2) | (0,3) | (0,4) |
(1,0) | (1,1) | |||
(2,0) | ||||
(3,0) | ||||
(4,0) | (4,4) |
思路:由于同一时间只能向下或者向右移动一步,所以(1,1)是它的上面的(0,1)和左面的(1,0)走过来的
但是最上面一行没有上面,最左面一行没有左面,因此f[i][0]和f[0][i]是要初始化值的
直到走到(4,4),它是f[R-1][C-1]
注意:一般地,初始化一个二维动态规划时,就去初始化第0行和第0列
public class Solution {
/**
* @param grid: a list of lists of integers.
* @return: An integer, minimizes the sum of all numbers along its path
*/
public int minPathSum(int[][] grid) {
if(grid==null || grid.length==0){
return 0;
}
if(grid[0]==null || grid[0].length==0){
return 0;
}
int R=grid.length;
int C=grid[0].length;
int[][] f=new int[R][C];
f[0][0]=grid[0][0]; //f[0][0]的情况
for(int i=1;i<R;i++){ //第0行和第0列初始化的时候也不都是仅仅抄写原来的值
f[i][0]=f[i-1][0]+grid[i][0]; //比方本题,是从最左上方向最右下方走
}
for(int i=1;i<C;i++){ //第0行有一个向右的指向
f[0][i]=f[0][i-1]+grid[0][i]; //第0列有一个向下的指向
}
for(int i=1;i<R;i++){
for(int j=1;j<C;j++){
f[i][j]=Math.min(f[i-1][j],f[i][j-1])+grid[i][j];
}
}
return f[R-1][C-1];
}
}
114.Unique Paths:点击打开链接
(0,0) | (0,1) | (0,2) | (0,3) | (0,4) |
(1,0) | (1,1) | |||
(2,0) | ||||
(3,0) | (3,4) | |||
(4,0) | (4,3) | (4,4) |
1 | 1 | 1 | 1 | 1 |
1 | 2 | 3 | 4 | |
1 | 3 | 6 | ||
1 | 4 | |||
1 |
思路:(1,1)点的2种方案是上一次(0,1)和(1,0)的每1种走法造成的结果
因此可知,(4,4)点的方案种数是上一次(3,4)和(4,3)的种数走法造成的结果
public class Solution {
/**
* @param n, m: positive integer (1 <= n ,m <= 100)
* @return an integer
*/
public int uniquePaths(int m, int n) {
if(m==0 || n==0){ //当只有一行或者一列的时候,只有一种方案,恒或者竖着一根线走到底
return 1;
}
int[][] f=new int[m][n];
for(int i=0;i<m;i++){ //由于f[0][0]=1,因此没有单独记录f[0][0]的情况
f[i][0]=1;
}
for(int i=0;i<n;i++){
f[0][i]=1;
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
f[i][j]=f[i-1][j]+f[i][j-1];
}
}
return f[m-1][n-1];
}
}
115.Unique Paths II:点击打开链接
思路:Unique Path的follow up,设置了障碍,只要把障碍处的方案种数记录为0即可。
public class Solution {
/**
* @param obstacleGrid: A list of lists of integers
* @return: An integer
*/
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
if(obstacleGrid==null || obstacleGrid.length==0){
return 0;
}
if(obstacleGrid[0]==null || obstacleGrid[0].length==0){
return 0;
}
int R=obstacleGrid.length;
int C=obstacleGrid[0].length;
int[][] f=new int[R][C];
for(int i=0;i<R;i++){ //第一竖行有障碍的情况,只要有障碍下面的都走不到,因此break
if(obstacleGrid[i][0]!=1){
f[i][0]=1;
}else{
break;
}
}
for(int i=0;i<C;i++){ //第一横行有障碍的情况,只要有障碍后面的都走不到,因此break
if(obstacleGrid[0][i]!=1){
f[0][i]=1;
}else{
break;
}
}
for(int i=1;i<R;i++){
for(int j=1;j<C;j++){
if(obstacleGrid[i][j]!=1){
f[i][j]=f[i-1][j]+f[i][j-1];
}else{ //但是其他位置有障碍的情况,这个位置后面的和下面的还可以被走到
f[i][j]=0; //因此只把这个位置的f[i][j]记为0,不能break
}
}
}
return f[R-1][C-1];
}
}
111.Climbing Stairs:点击打开链接
思路:爬楼梯题目介于Matrix和Sequence之间,用一维数组解决
解释:因为第三层台阶也就是f[2]是由第一层台阶f[0]或者第二层台阶f[1]跳上去的,所以f[2]=f[1]+f[0]
public class Solution {
/**
* @param n: An integer
* @return: An integer
*/
public int climbStairs(int n) {
int[] f=new int[n];
if(n<2){ //特殊情况先return值
return 1;
}
f[0]=1; //初始化第一层的值
f[1]=2; //初始化第二层的值
for(int i=2;i<n;i++){ //从第三层开始
f[i]=f[i-1]+f[i-2]; //因为要用到第一层和第二层记录的数据
}
return f[n-1];
}
}
272.Climbing Stairs II:点击打开链接
public class Solution {
/**
* @param n an integer
* @return an integer
*/
public int climbStairs2(int n) {
int[] f=new int[n];
if(n<2){
return 1;
}
if(n==2){
return 2;
}
f[0]=1; //初始化第一层的值
f[1]=2; //初始化第二层的值
f[2]=4; //初始化第三层的值
for(int i=3;i<n;i++){ //从第四层开始,之后都符合规律
f[i]=f[i-1]+f[i-2]+f[i-3];
}
return f[n-1];
}
}
630. Knight Shortest Path II:点击打开链接
方法二:BFS
思路:和Knight Shortest Path思路是一毛一样的,只是限制了马的走位,只留下右边的4个,起点是(0,0),终点是(n-1,m-1)
class Point { //自己写的内部类不可以加public,
public int x,y; //要不然就写在一个新file,名为Point里
public Point() {x=0;y=0;}
public Point(int x, int y) {
this.x=x;
this.y=y;
}
}
public class Solution {
/**
* @param grid a chessboard included 0 and 1
* @return the shortest path
*/
public int[] deltaX={1,-1,2,-2};
public int[] deltaY={2,2,1,1};
public int shortestPath2(boolean[][] grid) {
if(grid==null || grid.length==0){
return -1;
}
Point start=new Point(0,0);
Point end=new Point(grid.length-1,grid[0].length-1);
Queue<Point> queue=new LinkedList<>();
queue.offer(start);
int steps=0;
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
Point point=queue.poll();
if(point.x==end.x && point.y==end.y){
return steps;
}
for(int direction=0;direction<4;direction++){
Point movePoint=new Point(point.x+deltaX[direction],
point.y+deltaY[direction]);
if(isValid(grid, movePoint)){
queue.offer(movePoint);
grid[movePoint.x][movePoint.y]=true;
}
}
}
steps++;
}
return -1;
}
private boolean isValid(boolean[][] grid, Point point){
if (point.x < 0 || point.x >= grid.length) {
return false;
}
if (point.y < 0 || point.y >= grid[0].length) {
return false;
}
return grid[point.x][point.y] == false;
}
}