暴力递归与动态规划

题目一

假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2
开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)
如果机器人来到1位置,那么下一步只能往右来到2位置;
如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;
如果机器人来到中间位置,那么下一步可以往左走或者往右走;
规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种
给定四个参数 N、M、K、P,返回方法数。

暴力递归法

当前处在1 ~ N中的任意一个位置上,还有rest的步可以走,目标是p,

当我们认为递归的含义是这样的时候,就可以进行不同情况的讨论了

首先看Base Case 当发现还有0步可以走的时候,如果当前所在的位置就是目标位置那么,就获取了一种可以到达目标的方式

然后对可以走的位置进行分析

当来到1 的时候,根据题意,只能先右走去2的位置

当来的N的位置的时候,只能向左走,来的N的位置

中间的任意位置,就是向左和向右走的情况的和

public static int rootWork(int N,int M,int P,int K) {
		return process(N, M, K, P);
	}
	//递归的含义:一共有1-N的位置,目前在X位置,还有rest步可以走,目标是p
	public static int process(int N,int X,int rest,int P) {
		if(rest == 0) {
			return X == P ? 1 : 0;
		}
		if(X == 1) {
			return process(N, 2, rest - 1, P);
		}else if(X == N) {
			 return process(N, N - 1, rest - 1, P);
		}else {
			int p1 = process(N, X - 1, rest - 1, P);
			int p2 = process(N, X + 1, rest -1 ,P);
			return p1 + p2;
		}
	}

	public static void main(String[] args) {
		System.out.println(rootWork(5, 2, 4, 6));

	}

动态规划法

动态规划就是对暴力递归过程中产生的重复解进行去重的操作.那么观察上述递归过程,就有两个参数当前所处的位置和剩余的步数的值是在改变的.假设递归的函数为f(X,rest),那么可以发现,我们就是可以用一个二维数组来存储,不同rX和rest所对应的递归的值,从而从暴力递归改写为动态规划

public static int rootWork2(int N,int M,int P,int K) {
		//对上面递归过程进行分析,得出,X的变化范围就是1-N
		//rest的变范围就是0-k,所以使用一个N+1*K+1的数组就可以存储所以的递归值
		int[][] dp =new int[N + 1][K + 1];
		//当rest等于0时,只有X==P的时候,返回一 那么就可以得到dp[P][0] = 1;
		dp[P][0] = 1;
		//观察递归过程,发现要的到一个位置的递归返回值,就必须依赖它前一列的值
		//第0列已经的到了,所以直接从第一列开始填
		for(int rest = 1;rest <= K ; rest++) {
			for(int x = 1 ; x <= N ; x++) {
				if(x == 1) {
					dp[x][rest] = dp[2][rest - 1];
				}else if(x == N) {
					dp[x][rest] = dp[N - 1][rest - 1];
				}else {
					dp[x][rest] = dp[x + 1][rest - 1] + dp[x - 1][rest - 1];
				}
			}
		}
		return dp[M][K];
	}

	public static void main(String[] args) {
		System.out.println(rootWork(5, 2, 4, 6));
		System.out.println(rootWork2(5, 2, 4, 6));
	}

题目二

给定一个整型数组arr,代表数值不同的纸牌排成一条线
玩家A和玩家B依次拿走每张纸牌
规定玩家A先拿,玩家B后拿
但是每个玩家每次只能拿走最左或最右的纸牌
玩家A和玩家B都绝顶聪明
请返回最后获胜者的分数。

暴力递归法

	public static int win1(int[]arr) {
		if(arr == null || arr.length == 0) {
			return 0;
		}
		int p1 = f(arr, 0, arr.length - 1);
		int p2 = g(arr, 0 ,arr.length - 1);
		return Math.max(p1, p2);
	}
	//递归f()的含义:玩家在[L...R]的区间上,先手所能得到的最大分数
	public static int f(int[]arr,int L ,int R) {
		if(L == R) {
			return arr[L];
		}
		//第一种情况,玩家先手选择L位置的牌,然后L+1到R上后手
		int p1 = arr[L] + g(arr, L + 1, R);
		//第二种情况,玩家先手选择R位置的牌,然后在L到R+1上后手
		int p2 = arr[R] +g(arr, L, R - 1);
		return Math.max(p1, p2);
	}
	//递归g的含义,玩家在[L...R]上后手能获得的最大分数
	public static int g(int []arr,int L,int R) {
		if(L == R) {
			return 0;
		}
		//第一种情况,对手选择了L位置的牌,玩家在L+1到R上先手
		int p1 = f(arr, L + 1, R);
		//第二种情况,对手选择了R位置的牌,玩家在L到R -1先手
		int p2 = f(arr, L, R - 1);
		//因为题目中说,两位玩家都绝顶聪明,所以,当对手先手之后,玩家这能获得较少的分数
		return Math.min(p1, p2);
	}

动态规划法

因为这里的先手过程要依赖后手的过程所以需要建立两张表

public static int win2(int[]arr) {
		if(arr == null || arr.length == 0) {
			return 0;
		}
		//L和R的变化范围都是 0~arr.length - 1
		int N = arr.length - 1;
		int[][]f = new int[N][N];
		int[][]g = new int[N][N];
		//观察Base Case ,当L == R的时候f递归返回的是arr[L]的值,g递归返回的是0;
		for (int i = 0; i < N; i++) {
			f[i][i] = arr[i];
			g[i][i] = 0;
		}
		//对于普遍位置,f依赖g的对应位置下面和左面的值,g依赖f对应位置下面和左面的值
		//所以表就应该从下往上填
		for(int L = N - 2; L >= 0; L--) {
			for(int R = L + 1; R < N; R++) {//如果L > R 的话那么这个范围是错误位置不填
				int p1 = arr[L] + g[L + 1][R];
				int p2 = arr[R] + g[L][R - 1];
				f[L][R] = Math.max(p1, p2);
				p1 = f[L + 1][R];
				p2 = f[L][R - 1];
				g[L][R] = Math.min(p1, p2);
			}
		}
		int p1 = f[0][N - 1];
		int p2 = g[0][N - 1];
		return Math.max(p1, p2);
		
	}

题目三

给定两个长度都为N的数组weights和values,
weights[i]和values[i]分别代表 i号物品的重量和价值。
给定一个正数bag,表示一个载重bag的袋子,
你装的物品不能超过这个重量。
返回你能装下最多的价值是多少?

暴力递归法

public static int maxValue(int []wight,int[]value,int bag) {
		if(wight == null || value == null || wight.length == 0 || wight.length != value.length) {
			return 0;
		}
		return process(wight, value, 0, bag);
	}
	//递归的含义:目前来到了index位置拿取货物,之前的货物已经拿到,背包还有rest的空间
	//返回index位置开始可以随意的拿货物的最大价值数
	public static int process(int []wight,int []value,int index,int rest) {
		if(index == wight.length) {//没有货物了
			return 0;
		}
		//第一种情况,不要index位置的货物,能获得的最大价值数 ,返回
		int p1 = process(wight, value, index + 1, rest);
		//第二种情况,要index位置的货物,能获得的最大价值数,返回
		int p2 = Integer.MIN_VALUE;
		if(rest>=wight[index]) {//如果当前背包的剩余空间可以装下index位置的货物,才有第二种情况
			p2 =value[index] +  process(wight, value, index + 1, rest - wight[index]);
		}
		return Math.max(p1, p2);
	}

动态规划法

public static int maxValue2(int[]weight,int[]value,int bag) {
		if(weight == null || value == null || weight.length == 0 || weight.length != value.length) {
			return 0;
		}
		//观察递归:到的两个变惨,index和bag,index的变化范围是0~weight.length
		//bag的变化范围是0~bag
		int N = weight.length ;
		int[][]dp = new int[N + 1][bag + 1];
		//根据BaseCase得到dp[N][...]=0;
		//普遍位置依赖下一行的值 ,所以从下往上填表
		for(int index = N - 1 ; index >= 0; index--) {
			for(int rest = 0;rest <= bag; rest++) {
				int p1 = dp[index + 1][rest];
				int p2 = Integer.MIN_VALUE;
				if(rest >= weight[index]) {
					p2 =value[index] + dp[index + 1][rest - weight[index]];
				}
				dp[index][rest] = Math.max(p1, p2);
			}
		}
		return dp[0][bag];
	}

题目四

规定1和A对应、2和B对应、3和C对应...26和Z对应
那么一个数字字符串比如"111”就可以转化为:
"AAA"、"KA"和"AK"
给定一个只有数字字符组成的字符串str,返回有多少种转化结果

暴力递归法

public static int numberToChar(String string) {
		if(string == null || string.length() == 0) {
			return 0;
		}
		char[]str = string.toCharArray();
		return process(str, 0);
		
	}
	//递归的含义:目前来到了index位置,前面的位置已经转换完成,之后的位置可以任意挑选
	//返回有多少种转发
	public static int process(char[]str,int index) {
		if(index == str.length ) {//没有字符串可以被转化了,所以前面的决策是对的
			return 1;
		}
		if(str[index] == '0') {//说明前面的决策是错误的,因为'0'不能被单转为任意一个字符
			return 0;
		}
		//情况1:index位置的字符单转
		int p1 = process(str, index + 1);
		//情况2:index位置的字符和下一个字符一起转
		int p2 = 0;
		if(index + 1 < str.length 
				&& (str[index] - '0') *10 + (str[index + 1] - '0') <= 26) {
			p2 = process(str, index + 2);
		}
		return p1 + p2;
	}

动态规划法:

public static int numberToChar2(String string) {
		if(string == null || string.length() == 0) {
			return 0;
		}
		//分析暴力递归发现只有一个可变参数,范围是0 - string.length() ;
		char []str = string.toCharArray();
		int N = str.length;
		int[]dp = new int[N + 1];
		dp[N] = 1;
		for(int index = N - 1 ; index >= 0 ; index--) {
			if(str[index] == '0') {
				dp[index] = 0;
			}else {
				int p1 = dp[index + 1];
				int p2 = 0;
				if(index + 1 < str.length 
						&& (str[index] - '0') *10 + (str[index + 1] - '0') <= 26) {
					p2 = process(str, index + 2);
					p2 = dp[index + 2];
				}
				dp[index] = p1 + p2;
			}
		}
		return dp[0];
	}

题目五

给定一个字符串str,给定一个字符串类型的数组arr,出现的字符都是小写英文
arr每一个字符串,代表一张贴纸,你可以把单个字符剪开使用,目的是拼出str来
返回需要至少多少张贴纸可以完成这个任务。
例子:str= "babac",arr = {"ba","c","abcd"}
ba + ba + c  3  abcd + abcd 2  abcd+ba 2
所以返回2

暴力递归法

public static int minStickers(String[] stickers, String target) {
		 if(stickers == null || stickers.length == 0 || target == null || target.length() == 0) {
			return 0; 
		 }
		 int ans = process(stickers, target);
			return ans == Integer.MAX_VALUE ? -1 : ans;
	    }
	 //在sticksers 中任意选择,得到rest的最少的张数
	 public static int process(String[]stickers,String rest) {
		 if(rest.length() == 0 || rest.equals("")){
			 return 0;
		 }
		 int min = Integer.MAX_VALUE;
		 for(String frist : stickers) {//一数组中的任意一个字符串作为第一个,进行选择得到最小的结果
			 String ans = minus(rest, frist);
			 //当剪完之后得到的字符串和目标字符串一样的时候
			 //说明当前的字符串不包含目标串中的字符
			 //跳过当前字符串,去下一个字符串
			 if(ans.length() != rest.length()) {
				 //能够得到rest的最少的张数,就是一每个字符串为开始位置得到的最小张数
				  min = Math.min(min, process(stickers, ans));
			 }
		 }
		 //如果以当前字符串为开始字符串不能的到结果则不用加当前字符串代表的张数
		 return min + (min == Integer.MAX_VALUE ? 0 : 1);
	 }
	 public static String minus(String a,String b) {
		char[]str1 = a.toCharArray();
		char[]str2 = b.toCharArray();
		int []count = new int[26];
		for(char ch: str1) {
			count[ch - 'a']++;
		}
		for(char ch: str2) {
			count[ch - 'a']--;
		}
		StringBuilder builder =  new StringBuilder();
		//builder.append("");
		for (int i = 0; i < count.length; i++) {
			if (count[i] > 0) {
				for (int j = 0; j < count[i]; j++) {
					builder.append((char) (i + 'a'));
				}
			}
		}
		System.out.println(builder.toString());
		return builder.toString();
	 }

动态规划法

这道题无法使用严格表结构,所以用记忆化搜索搞定

 public static int minStickers(String[] stickers, String target) {
		 if(stickers == null || stickers.length == 0 || target == null || target.length() == 0) {
			return 0; 
		 }
		 HashMap<String, Integer>dpHashMap  = new HashMap<>();
		 int ans = process(stickers, target,dpHashMap);
			return ans == Integer.MAX_VALUE ? -1 : ans;
	    }
	 //在sticksers 中任意选择,得到rest的最少的张数
	 public static int process(String[]stickers,String rest,HashMap<String, Integer>dpHashMap) {
		 if(dpHashMap.containsKey(rest)) {
			 return dpHashMap.get(rest);
		 }
		 if(rest.length() == 0 || rest.equals("")){
			 return 0;
		 }
		 int min = Integer.MAX_VALUE;
		 for(String frist : stickers) {//一数组中的任意一个字符串作为第一个,进行选择得到最小的结果
			 String ans = minus(rest, frist);
			 //当剪完之后得到的字符串和目标字符串一样的时候
			 //说明当前的字符串不包含目标串中的字符
			 //跳过当前字符串,去下一个字符串
			 if(ans.length() != rest.length()) {
				 //能够得到rest的最少的张数,就是一每个字符串为开始位置得到的最小张数
				  min = Math.min(min, process(stickers, ans,dpHashMap));
			 }
		 }
		 //如果以当前字符串为开始字符串不能的到结果则不用加当前字符串代表的张数
		 return min + (min == Integer.MAX_VALUE ? 0 : 1);
	 }
	 public static String minus(String a,String b) {
		char[]str1 = a.toCharArray();
		char[]str2 = b.toCharArray();
		int []count = new int[26];
		for(char ch: str1) {
			count[ch - 'a']++;
		}
		for(char ch: str2) {
			count[ch - 'a']--;
		}
		StringBuilder builder =  new StringBuilder();
		//builder.append("");
		for (int i = 0; i < count.length; i++) {
			if (count[i] > 0) {
				for (int j = 0; j < count[i]; j++) {
					builder.append((char) (i + 'a'));
				}
			}
		}
		System.out.println(builder.toString());
		return builder.toString();
	 }

 题目六

给定两个字符串str1和str2,
返回这两个字符串的最长公共子序列长度

比如 : str1 = “a12b3c456d”,str2 = “1ef23ghi4j56k”
最长公共子序列是“123456”,所以返回长度6

暴力递归法

public int longestCommonSubsequence(String text1, String text2) {
		 if(text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0 ) {
			 return 0;
		 }
		 char[]str1 = text1.toCharArray();
		 char[]str2 = text2.toCharArray();
		return process(str1, str2, str1.length - 1, str2.length - 1);
	    }
	 //递归的含义:在字符串1 [0...i]和字符串2[0...j]位置上找最长的公共子序列返回
	 public static int process(char[]str1,char[]str2,int i,int j) {
		 if(i == 0 && j == 0) {//两个字符串都只有一个字符
			 //这两个字符相等返回1否则返回0
			 return str1[i] == str2[j] ? 1 : 0;
		 }
		 if(i == 0) {//当str1在0位置的时候
			 //两种情况:
			 //1:str10位置的字符个str2[j]相同,那么最长公共子序列就是1
			
			if(str1[i] == str2[j]) {
				return 1;
			} 
			//2:str1[0]位置的字符和str2[j]不相同,那就计算str1[0]和str2[0..j-1]的最长公共子序列
			return process(str1, str2, i, j - 1);
		 }
		 //j== 0同理
		 if(j == 0) {
			 if(str1[i] == str2[j]) {
				 return 1;
			 }
			 return process(str1, str2, i - 1, j);
		 }
		 //当都不等于零的时候,有三种情况
		 //1.str2[j]这个字符不在公共子序列内
		 int p1 = process(str1, str2, i, j - 1);
		 //2.str1[i]这个字符不在公共子序列内
		 int p2 = process(str1, str2, i - 1, j);
		 //2.str1[i]和str2[j]相等
		 int p3 = 0;
		 if(str1[i] == str2[j]) {
			p3 = 1 + process(str1, str2, i - 1, j - 1);
		 }
		 return Math.max(p1, Math.max(p2, p3));
	 }
	 

动态规划法

public int longestCommonSubsequence(String text1, String text2) {
		 if(text1 == null || text2 == null || text1.length() == 0 || text2.length() == 0 ) {
			 return 0;
		 }
		 char[]str1 = text1.toCharArray();
		 char[]str2 = text2.toCharArray();
		return process(str1, str2, str1.length - 1, str2.length - 1);
	    }
	 //递归的含义:在字符串1 [0...i]和字符串2[0...j]位置上找最长的公共子序列返回
	 public static int process(char[]str1,char[]str2,int i,int j) {
		 if(i == 0 && j == 0) {//两个字符串都只有一个字符
			 //这两个字符相等返回1否则返回0
			 return str1[i] == str2[j] ? 1 : 0;
		 }
		 if(i == 0) {//当str1在0位置的时候
			 //两种情况:
			 //1:str10位置的字符个str2[j]相同,那么最长公共子序列就是1
			
			if(str1[i] == str2[j]) {
				return 1;
			} 
			//2:str1[0]位置的字符和str2[j]不相同,那就计算str1[0]和str2[0..j-1]的最长公共子序列
			return process(str1, str2, i, j - 1);
		 }
		 //j== 0同理
		 if(j == 0) {
			 if(str1[i] == str2[j]) {
				 return 1;
			 }
			 return process(str1, str2, i - 1, j);
		 }
		 //当都不等于零的时候,有三种情况
		 //1.str2[j]这个字符不在公共子序列内
		 int p1 = process(str1, str2, i, j - 1);
		 //2.str1[i]这个字符不在公共子序列内
		 int p2 = process(str1, str2, i - 1, j);
		 //2.str1[i]和str2[j]相等
		 int p3 = 0;
		 if(str1[i] == str2[j]) {
			p3 = 1 + process(str1, str2, i - 1, j - 1);
		 }
		 return Math.max(p1, Math.max(p2, p3));
	 }
	 

 题目六

给定一个字符串str,返回这个字符串的最长回文子序列长度
比如 : str = “a12b3c43def2ghi1kpm”
最长回文子序列是“1234321”或者“123c321”,返回长度7

暴力递归法

static int  longestPalindromeSubseq(String s) {
	    if(s == null || s.length() == 0) {
	        	return 0;
	        }    
	   char[]str = s.toCharArray();
	   return process(str, 0, str.length - 1);     
	  }
	//递归的含义:str[L ... R]上的最长公共子序列
	public static int process(char[]str,int L ,int R) {
		System.out.println(L + " " + R);
		if(L > R) {
			return 0;
		}
		if(L == R) {
			return 1;
		}
		//情况1:这个回文子序列中不包含R位置的字符
		int p1 = process(str, L, R - 1);
		//情况2:这个回文子序列不包含L位置的字符
		int p2 = process(str, L + 1, R);
		//情况3:这个回文子序列包含L位置的字符也包含R位置的字符
		int p3 = 0;
		if(str[L] == str[R]) {
		p3 = 2 + process(str, L + 1, R - 1);
		}
		return Math.max(Math.max(p1, p2), p3);
	}

动态规划法

	
	public static int  longestPalindromeSubseq2(String s) {
		  if(s == null || s.length() == 0) {
	        	return 0;
	        }    
	   char[]str = s.toCharArray();
	   int N = str.length ;
	   int[][]dp = new int[N ][N];
	   for(int i = 0; i < N; i++) {
		   dp[i][i] = 1;
	   }
	   for(int L = N - 2 ; L >= 0; L-- ) {
		   for(int R = L + 1 ; R < N ; R++) {
			   int p1 = dp[L][R - 1];
			   int p2 = dp[L + 1][R];
			   int p3 = 0 ;
			   if(str[L] == str[R]) {
				   p3 = 2 + dp[L +  1][R - 1];
			   }
			   dp[L][R] = Math.max(Math.max(p1, p2), p3);
		   }
	   }
	   return dp[0][N - 1];
	}

题目七

一个象棋的棋盘,
然后把整个棋盘放入第一象限,棋盘的最左下角是(0,0)位置
那么整个棋盘就是横坐标上9条线、纵坐标上10条线的区域
给你三个 参数 x,y,k
返回“马”从(0,0)位置出发,必须走k步
最后落在(x,y)上的方法数有多少种?

暴力递归法

public static int jump(int x,int y ,int k) {
		return process(x, y, k, 0, 0);
	}
	//递归的含义,现在在a,b位置上,要到(x,y)上去,还有rest步可以走,
	//返回所有的方法数
	public static int process(int x,int y ,int rest,int a,int b) {
		if(a == x && y == b && rest == 0) {
			return 1;
		}
		if(a < 0 || a > 9 || b < 0 || b > 8 || rest < 0) {
			return 0;
		}
		int ways = 0;
		ways += process(x , y , rest - 1, a + 2, b + 1);
		ways += process(x, y, rest - 1, a + 2, b - 1);
		ways += process(x, y, rest - 1, a - 2, b + 1);
		ways += process(x, y, rest - 1, a - 2, b - 1);
		ways += process(x, y, rest - 1, a + 1, b - 2);
		ways += process(x, y, rest - 1, a + 1, b + 2);
		ways += process(x, y, rest - 1, a - 1, b + 2);
		ways += process(x, y, rest - 1, a - 1, b - 2);
		return ways;
	}

动态规划法

public static int dp(int a, int b, int k) {
		int[][][] dp = new int[10][9][k + 1];
		dp[a][b][0] = 1;
		for (int rest = 1; rest <= k; rest++) {
			for (int x = 0; x < 10; x++) {
				for (int y = 0; y < 9; y++) {
					int ways = pick(dp, x + 2, y + 1, rest - 1);
					ways += pick(dp, x + 1, y + 2, rest - 1);
					ways += pick(dp, x - 1, y + 2, rest - 1);
					ways += pick(dp, x - 2, y + 1, rest - 1);
					ways += pick(dp, x - 2, y - 1, rest - 1);
					ways += pick(dp, x - 1, y - 2, rest - 1);
					ways += pick(dp, x + 1, y - 2, rest - 1);
					ways += pick(dp, x + 2, y - 1, rest - 1);
					dp[x][y][rest] = ways;
				}
			}
		}
		return dp[0][0][k];
	}

	public static int pick(int[][][] dp, int x, int y, int rest) {
		if (x < 0 || x > 9 || y < 0 || y > 8) {
			return 0;
		}
		return dp[x][y][rest];
	}

题目八

arr是货币数组,其中的值都是正数。再给定一个正数aim。
每个值都认为是一张货币,
即便是值相同的货币也认为每一张都是不同的,
返回组成aim的方法数
例如:arr = {1,1,1},aim = 2
第0个和第1个能组成2,第1个和第2个能组成2,第0个和第2个能组成2
一共就3种方法,所以返回3

暴力递归

	public static int coinWays(int[] arr, int aim) {
		return process(arr, aim, 0);
	}
	//现在选到了那个位置,还剩多少钱,返回组成剩余钱数的方法数
	public static int process(int[]arr,int rest,int index) {
		if(rest < 0) {
			return 0;
		}
		if(rest == 0) {
			return 1;
		}
		if(index == arr.length) {//没有钱了
			return 0;
		}
		int p1 = process(arr, rest, index + 1);
		int p2 = process(arr, rest - arr[index], index + 1);
		return p1 + p2;
	}

动态规划法

public static int dp(int[] arr, int aim) {
		if (aim == 0) {
			return 1;
		}
		int N = arr.length;
		int[][] dp = new int[N + 1][aim + 1];
		dp[N][0] = 1;
		for (int index = N - 1; index >= 0; index--) {
			for (int rest = 0; rest <= aim; rest++) {
				dp[index][rest] = dp[index + 1][rest] + (rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : 0);
			}
		}
		return dp[0][aim];
	}

题目九

arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。
每个值都认为是一种面值,且认为张数是无限的。
返回组成aim的方法数
例如:arr = {1,2},aim = 4
方法如下:1+1+1+1、1+1+2、2+2
一共就3种方法,所以返回3

暴力递归法

public static int coinsWay(int[] arr, int aim) {
		if (arr == null || arr.length == 0 || aim < 0) {
			return 0;
		}
		return process(arr, aim, 0);
	}
	//递归含义:目前来的了index位置,要组成rest的钱,每个位置的钱都可以无限使用,
	//返回一共有多少方法数
	public static int process(int []arr,int rest,int index) {
		if(rest == 0) {
			return 1;
		}
		if(index == arr.length) {
			return 0;
		}
		
		int ways = 0;
		for(int i = 0; i*arr[index] <= rest; i++) {
			ways += process(arr, rest - i * arr[index], index + 1);
		}
		return ways ;
	}
	

动态规划法

public static int dp(int[]arr,int aim) {
		if (arr == null || arr.length == 0 || aim < 0) {
			return 0;
		}
		int N = arr.length;
		int[][]dp = new int[N +  1][aim + 1];
		for(int index = 0; index <= N ; index++) {
			dp[index][0] = 1;
		}
		for(int index = N - 1 ; index >=0; index--) {
			for(int rest = 1; rest <= aim ; rest++) {
				dp[index][rest] = dp[index + 1][rest];
				if(rest - arr[index] >= 0) {
					dp[index][rest] += dp[index][rest - arr[index ]];
				}
			}
		}
		return dp[0][aim];
	}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值