把握要点:动态规划问题要注意保存子问题的值,并且在过程中,不要忘记使用它
经典面试题10
题目描述: 和第九题的框架相同,机器人还是要从网格左上角到达右下角,
但是网格中添加了障碍物,障碍物用1表示
- 状态定义:
子状态从(0,0),(0,1),(1,0)…(m-1,n-1)的路径数
F(i,j):从F(0,0)到F(i,j)的路径数 - **状态转移方程的定义:
当这个点为0:F(i,j)=F(i,j-1)+F(i-1,j)
当这个点位1:0 ** - 状态的初始化:F(i,0)=F(o,j)={0 OR 1}
- 返回结果:F(m-1,n-1)
注意:如果此点为障碍物,则后面点都到达不了
public class Solution {
public int uniquePathsWithObstacles(int[][] obstacles) {
if(obstacles==null){
return 0;
}
int m=obstacles.length;
int n=obstacles[0].length;
//用来储存每一个点的路径值
int[][] temp=new int[m][n];
//初始化,第一行,第一列的值若不为1(障碍),则到达的路径数为1
for(int i=0;i<m;i++){
if(obstacles[i][0]==1){
break;
}else {
temp[i][0]=1;
}
}
for(int j=0;j<n;j++){
if(obstacles[0][j]==1){
break;
}else {
temp[0][j]=1;
}
}
for(int i=1;i<m;i++){
for (int j=1;j<n;j++){
if(obstacles[i][j]==1){
//注意,此处是continue,只跳出本层循环
continue;
}else {
temp[i][j]=temp[i-1][j]+temp[i][j-1];
}
}
}
return temp[m-1][n-1];
}
}
经典面试题11
题目描述: 题目描述:
给定一个m*n的网格,网格用非负数填充,找到一条从左上角到右下角的最短路径。
注:每次只能向下或者向右移动。
- 状态定义:
子状态从(0,0),(0,1),(1,0)…(m-1,n-1)的路径数
F(i,j):从F(0,0)到F(i,j)的路径数 - 状态转移方程的定义:
F(i,j)=min(F(i-1)(j),F(i)(j-1))+a[][j] - 状态的初始化:F(0,0)=a(0,0)
第一行:F(i,0)=F(i-1,0)+a[i][0]
第一列:F(0,j)=F(0,j-1)+a[0][j] - 返回结果:F(m-1,n-1)
public class Solution {
public int minPathSum(int[][] grid) {
if(grid==null){
return 0;
}
int m=grid.length;
int n=grid[0].length;
int [][]temp=new int[m][n];
//初始化
temp[0][0]=grid[0][0];
for(int i=1;i<m;i++){
temp[i][0]=temp[i-1][0]+grid[i][0];
}
for(int j=1;j<n;j++){
temp[0][j]=temp[0][j-1]+grid[0][j];
}
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
temp[i][j]=Math.min(temp[i-1][j],temp[i][j-1])+grid[i][j];
}
}
return temp[m-1][n-1];
}
}
经典面试题12
题目描述: 题目描述:
有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
问最多能装入背包的总价值是多大?
- 状态定义:
F(i, j): 前i个物品放入大小为j的背包中所获得的最大价值 - 状态转移方程的定义:
对于第i个商品:
如果装不下:此时的价值与前i-1个的价值是一样的F(i,j) = F(i-1,j)
如果可以装下:需要在两种选择中找最大的
F(i, j) = max{F(i-1,j), F(i-1, j - A[i]) + V[i]}
F(i-1,j): 表示不把第i个物品放入背包中, 所以它的价值就是前i-1个物品放入大小为j的背包的最大价值
F(i-1, j - A[i]) + V[i]:表示把第i个物品放入背包中,价值增加V[i],但是需要腾出A[i]的大小放第i个商品 - 状态的初始化:F(0,0)=a(0,0)
第0行和第0列都为0,(没有物品,没有容量)因此表示物品的价值都为0
F(0,j) = F(i,0) = 0 - 返回结果:F(n,m)
public class Solution {
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @param V: Given n items with value V[i]
* @return: The maximum value
*/
public int backPackII(int m, int[] A, int[] V) {
//运行条件,必须有值
if(A==null&&V==null&&m<1){
return 0;
}
//多开辟一行一列用于存储初始值,A的大小,m的大小都代表真实的值
int N=A.length+1;
int M=m+1;
//存储背包的价值
int [][] result=new int[N][M];
//第0行(表示没有物品),第0列(表示没有背包容量),因此价值都为0,
for(int i=0;i<N;i++){
result[i][0]=0;
}
for(int j=0;j<M;j++){
result[0][j]=0;
}
for(int i=1;i<N;i++){
for(int j=1;j<M;j++){
//如果第i商品的重量大于背包的容量,放不下,所以(i,j)的最大价值和(i-1,j)相同
//注意边界,循环从1开始的,因此A[0]=A[1-1]
if(A[i-1]>j){
result[i][j]=result[i-1][j];
}else {
//否则要么装,要么不装
//装
int a=result[i-1][j-A[i-1]]+V[i-1];
//不装
int b=result[i-1][j];
//在装与不装之间选出最大值
result[i][j]=Math.max(a,b);
}
}
}
//返回装入前N个商品,物品大小为m的最大价值
return result[N-1][m];
}
}
经典面试题13
题目描述: 题目描述:
给定一个字符串 s,把 s 分割成一系列的子串,分割的每一个子串都为回文串
返回最小的分割次数
比如,给定 s = “aab”,
返回1,因为一次cut就可以产生回文分割[“aa”,“b”]
- 状态定义:
F(i): 前i个字符串的最小分割次数 - 状态转移方程的定义:
F(i) = min(F(i), 1 + F(j) (j<i)
上式表示如果从j+1到i判断为回文字符串,且已经知道从第1个字符
到第j个字符的最小切割数,那么只需要再切一次,就可以保证
1–>j, j+1–>i都为回文串。 - 状态的初始化:F(i)=i-1;(‘a’)不需要切割
- 返回结果:F(n)
public class Solution {
public int minCut(String s) {
if(s.isEmpty()){
return 0;
}
int length=s.length();
//用于储存到每个字母的最小分割次数
int [] result=new int[length+1];
//初始化,注意边界,0代表0个元素,
for(int i=0;i<length+1;i++){
result[i]=i-1;
}
for(int i=1;i<length+1;i++){
for(int j=0;j<i;j++){
///F(i) = min{F(i), 1 + F(j)},
//从最长串判断,如果从第j+1到i为回文字符串
//则再加一次分割,从1到j,j+1到i的字符就全部分成了回文字符串
if (isPalindrome(s, j, i - 1)){
result[i]=Math.min(result[i],1+result[j]);
}
}
}
return result[length];
}
private boolean isPalindrome(String s, int j, int i) {
while(j<i){
if(s.charAt(j)!=s.charAt(i)){
return false;
}
j++;
i--;
}
return true;
}
}