leetcode之记忆化搜索刷题总结1
1-青蛙跳台阶问题
题目链接:题目链接戳这里!!!
思路1:动态规划思想
f[i]表示跳到第i层的种类数,因为每次可以调一层和二层,故等于跳到第i-1层的种类数+跳到第i-2层的种类数。递推表达式如下:
f[i]=f[i-1]+f[i-2]
class Solution {
int mod = 1000000007;
public int numWays(int n) {
if(n<=1){
return 1 ;
}
int [] f = new int [n+1] ;
f[0]=f[1] = 1 ;
for(int i=2; i<=n; i++){
f[i] = f[i-1]%mod + f[i-2]%mod ;
}
return f[n]%mod ;
}
}
思路2:记忆化搜索,也就是记忆递归,直接递归由于包含大量重复计算,会超时。
class Solution {
int mod = 1000000007;
public int numWays(int n) {
int [] vis = new int [n+1] ;
Arrays.fill(vis,-1) ;
return dfs(vis,n) ;
}
public int dfs(int [] vis, int n){
if(vis[n]!=-1){
return vis[n] ;
}
if(n==0 || n==1){
return 1 ;
}
vis[n] = dfs(vis,n-1)%mod+dfs(vis,n-2)%mod ;
return vis[n]%mod ;
}
}
2-三步问题
题目链接:题目据链接戳这里!!!
思路1:动态规划
和上一题思路一样,只不过这一题递推式是三个相加
class Solution {
int mod = 1000000007 ;
public int waysToStep(int n) {
if(n==1){
return 1 ;
}else if(n==2){
return 2 ;
}else if(n==3){
return 4 ;
}
int [] f = new int [n+1] ;
f[1]=1;
f[2]=2;
f[3]=4;
for(int i=4; i<=n; i++){
f[i] = ((f[i-1]%mod + f[i-2]%mod)%mod + f[i-3]%mod)%mod ;
}
return f[n]%mod;
}
}
思路2:记忆化搜索
class Solution {
int mod = 1000000007 ;
public int waysToStep(int n) {
int [] vis = new int [n+1] ;
Arrays.fill(vis,-1) ;
return dfs(vis,n) ;
}
public int dfs(int [] vis, int n){
if(vis[n]!=-1){
return vis[n] ;
}
if(n==1){
return 1 ;
}
if(n==2){
return 2 ;
}
if(n==3){
return 4 ;
}
vis[n] = ((dfs(vis,n-1)%mod + dfs(vis,n-2)%mod)%mod + dfs(vis,n-3)%mod)%mod;
return vis[n]%mod ;
}
}
3-第N个泰波那契数列
题目链接:题目链接戳这里!!!
注意:不是斐波那契数列,哈哈,不过思路一样的,记忆化递归。
class Solution {
int [] vis ;
public int tribonacci(int n) {
vis = new int [n+1] ;
Arrays.fill(vis,-1) ;
return dfs(n) ;
}
public int dfs(int n){
if(vis[n]!=-1){
return vis[n] ;
}
if(n==0){
return 0 ;
}
if(n==1 || n==2){
return 1 ;
}
vis[n] = dfs(n-1) + dfs(n-2) + dfs(n-3) ;
return vis[n] ;
}
}
这题用动态规划也是一样的,不再赘述。
4-斐波那契数列
题目链接:题目链接戳这里!!!
思路:记忆化搜索,动态规划也可以。
class Solution {
int [] vis ;
int mod = 1000000007 ;
public int fib(int n) {
vis = new int [n+1] ;
Arrays.fill(vis,-1) ;
return dfs(n) ;
}
public int dfs(int n){
if(vis[n]!=-1){
return vis[n] ;
}
if(n==0){
return 0 ;
}
if(n==1){
return 1 ;
}
vis[n] = (dfs(n-1)%mod + dfs(n-2)%mod)%mod ;
return vis[n]%mod ;
}
}
5-单词拆分
题目链接:题目链接戳这里!!!
思路1:动态规划
dp[i]表示前i位是否可以用wordDict表示
初始化 dp[0]=true,空字符可以被表示
遍历字符串的所有子串,如果dp[j]为true,则说明前j位可以被wordDict表示,此时若wordDict中存在s.substring(j,i),则dp[i]为true,即前i位可以用wordDict表示。
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
//dp[i]表示前i位是否可以用wordDict表示
int n = s.length() ;
boolean [] dp = new boolean [n+1] ;
dp[0] = true ;
Set<String> set = new HashSet<>(wordDict) ;
for(int i=1; i<dp.length; i++){
for(int j=0; j<i; j++){
if(dp[j] && set.contains(s.substring(j,i))){
dp[i] = true;
break ;
}
}
}
return dp[n] ;
}
}
6-矩阵中的最长递增路径
题目链接:题目链接戳这里!!!
思路:沿着四个方向记忆化搜索,每次将搜索到的最大值返回,下次如果在搜索到该位置,直接返回。
class Solution {
//记忆化搜索
int [] dx = {-1,1,0,0} ;
int [] dy = {0,0,-1,1} ;
int [][] temp ;
public int longestIncreasingPath(int[][] matrix) {
int max = Integer.MIN_VALUE ;
temp = new int [matrix.length][matrix[0].length] ;
for(int i=0; i<temp.length; i++){
for(int j=0; j<temp[0].length; j++){
temp[i][j] = -1 ;
}
}
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
max = Math.max(max,dfs(matrix,i,j)) ;
}
}
return max ;
}
public int dfs(int [][] matrix, int x, int y){
if(temp[x][y]!=-1){
return temp[x][y] ;
}
temp[x][y] = 1 ;
int res = 0 ;
for(int i=0; i<4; i++){
int tx = x + dx[i] ;
int ty = y + dy[i] ;
if(tx<0 || ty<0 || tx>matrix.length-1 || ty>matrix[0].length-1 || matrix[tx][ty]<=matrix[x][y]){
continue ;
}
res = Math.max(res,dfs(matrix,tx,ty)) ;
}
temp[x][y] += res ;
return temp[x][y] ;
}
}
这样写看着更舒服,思路是一样的,记忆化搜索
class Solution {
//记忆化搜索
int [] dx = {-1,1,0,0} ;
int [] dy = {0,0,-1,1} ;
int [][] temp ;
public int longestIncreasingPath(int[][] matrix) {
int max = Integer.MIN_VALUE ;
temp = new int [matrix.length][matrix[0].length] ;
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
max = Math.max(max,dfs(matrix,i,j,temp)) ;
}
}
return max ;
}
public int dfs(int [][] matrix, int x, int y, int [][] temp){
if(temp[x][y]!=0){
return temp[x][y] ;
}
temp[x][y] = 1 ;
int res = 0 ;
for(int i=0; i<4; i++){
int tx = x + dx[i] ;
int ty = y + dy[i] ;
if(tx>=0 && ty>=0 && tx<=matrix.length-1 && ty<=matrix[0].length-1 && matrix[tx][ty]>matrix[x][y]){
temp[x][y] = Math.max(temp[x][y],dfs(matrix,tx,ty,temp)+1) ;
}
}
return temp[x][y] ;
}
}
7-统计所有可行路径
题目链接:题目链接戳这里!!!
思路:记忆化搜索
我使用数组memo[start][fuel]记录从start开始到finish一共有fuel燃料可以有多少种行驶路径。
最初数组全部填充-1,每次搜索到填充0,对于其它非开始顶点,如果燃料够用,就尝试着走,如果到不了终点,则不可行,达到终点则方案数加1,如果之前已经得到了方案数,下一次则直接返回方案数。
class Solution {
int [][] memo;
int mod = 1000000007;
public int countRoutes(int[] locations, int start, int finish, int fuel) {
memo = new int [locations.length][fuel+1] ;
for(int [] arr : memo){
Arrays.fill(arr,-1) ;
}
return dfs(locations,start,finish,fuel) ;
}
public int dfs(int [] locations, int start, int finish, int fuel){
if(memo[start][fuel]!=-1){
return memo[start][fuel] ;
}
memo[start][fuel] = 0 ;
if(Math.abs(locations[start]-locations[finish]) > fuel){
return 0 ;
}
for(int i=0; i<locations.length; i++){
if(i!=start){
int cost ;
if((cost=Math.abs(locations[start]-locations[i]))<=fuel){
memo[start][fuel] += dfs(locations,i,finish,fuel-cost) ;
memo[start][fuel] %= mod ;
}
}
}
if(start==finish){
memo[start][fuel]++ ;
memo[start][fuel]%=mod ;
}
return memo[start][fuel] ;
}
}
8-最长递增路径
题目链接:题目链接戳这里!!!
思路:记忆化搜索,哈哈,这一题竟然重复了,
memo[x][y]是记忆化数组,表示到达当前坐标位置时的最长递增路径长度。从每个点开始,沿着四个方向记忆化搜索,找出每轮搜索找到的最大值,再将所有的最大值进行比较,选出最大的即可。
class Solution {
int [] dx = {-1,1,0,0} ;
int [] dy = {0,0,-1,1} ;
int [][] memo ;
public int longestIncreasingPath(int[][] matrix) {
int max = Integer.MIN_VALUE ;
memo = new int [matrix.length][matrix[0].length] ;
for(int [] arr : memo){
Arrays.fill(arr,-1) ;
}
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
max = Math.max(max, dfs(matrix,i,j)) ;
}
}
return max ;
}
public int dfs(int [][] matrix, int x, int y){
if(memo[x][y] != -1){
return memo[x][y] ;
}
memo[x][y] = 1 ;
for(int i=0; i<4; i++){
int tx = x + dx[i] ;
int ty = y + dy[i] ;
if(tx<0 || ty<0 || tx>matrix.length-1 || ty>matrix[0].length-1 || matrix[tx][ty]<=matrix[x][y]){
continue ;
}
memo[x][y]= Math.max(memo[x][y],dfs(matrix,tx,ty)+1) ;
}
return memo[x][y] ;
}
}
9-吃掉n个橘子的最少天数
题目链接:题目链接戳这里!!!
思路:记忆化递归
map.get(n)表示吃掉n个橘子所需的最少天数
用f(i)表示吃掉i个橘子所需要的最少天数。
class Solution {
Map<Integer, Integer> map = new HashMap<>() ;
public int minDays(int n) {
if(n<=1){
return n ;
}
if(map.containsKey(n)){
return map.get(n) ;
}
map.put(n,Math.min(n%2+1+minDays(n/2),n%3+1+minDays(n/3))) ;
return map.get(n) ;
}
}
10-有向图中最大颜色值
题目链接:题目链接戳这里!!!
思路:邻接表+dfs+dp
dfs+flag:判断是否成环
dp[i][26]:动态规划记录以i为起点的有效路径中,各颜色的最大值
class Solution {
char [] color ;
int [][] dp ;
int res=0 ;
public int largestPathValue(String colors, int[][] edges) {
int [] flag = new int [colors.length()] ;
dp = new int [colors.length()][26] ;//以i为起点的有效路径中,各颜色的最大值
color = colors.toCharArray() ; //记录每个点的颜色
List<List<Integer>> adj = new ArrayList<>() ;
for(int i=0; i<colors.length(); i++){
adj.add(new ArrayList<>()) ;
}
for(int [] edge : edges){ //建立邻接表
adj.get(edge[0]).add(edge[1]) ;
}
for(int i=0; i<colors.length(); i++){
if(!dfs(adj,i,flag)){
return -1 ;
}
}
return res ;
}
public boolean dfs(List<List<Integer>> adj, int i, int [] flag){
if(flag[i]==-1){ //上一轮访问过
return true ;
}
if(flag[i]==1){ //有环
return false ;
}
flag[i] = 1 ;
for(int j : adj.get(i)){
if(!dfs(adj,j,flag)){
return false ;
}
for(int k=0; k<26; k++){
dp[i][k] = Math.max(dp[i][k],dp[j][k]) ;
}
}
flag[i] = -1 ;
dp[i][color[i]-'a'] += 1 ;
res = Math.max(dp[i][color[i]-'a'],res) ;
return true ;
}
}