什么暴力递归可以优化?

暴力递归和动态规划的关系

如何找到某个问题的动态规划方式?

设计暴力递归过程的原则

四种尝试模型

暴力递归到动态规划的套路

动态规划的进一步优化
1)空间压缩
2)状态化简
3)四边形不等式
4)其他优化技巧
题目一

public class RobotWalk{
public static int ways1(int N,int start,int aim,int K){
if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1){
return -1;
}
return proccess1(start,K,aim,N);
}
// 机器人当前来到的位置是cur,
// 机器人还有rest步需要去走,
// 最终的目标是aim,
// 有哪些位置?1~N
// 返回:机器人从cur出发,走过rest步之后,最终停在aim的方法数,是多少?
private static int proccess1(int cur,int rest,int aim,int n){
if (rest == 0){
return cur == aim ? 1:0;
}
if (cur == 1){
return proccess1(cur+1,rest-1,aim,n);
}
if (cur == n){
return proccess1(cur-1,rest-1,aim,n);
}
return proccess1(cur +1,rest-1,aim,n) + proccess1(cur-1,rest-1,aim,n);
}
public static int ways2(int N,int start,int aim,int K){
if (N < 2 || start < 1 || start > N || aim < 1 || aim > N || K < 1){
return -1;
}
int[][] dp = new int[N+1][K+1];
for (int i = 0; i < N + 1; i++) {
for (int j = 0; j < K + 1; j++) {
dp[i][j] = -1;
}
}
return proccess2(start,K,aim,N,dp);
}
//记忆搜索法
private static int proccess2(int cur,int rest,int aim,int n,int[][] dp){
if (dp[cur][rest] != -1){
return dp[cur][rest];
}
int ans = 0;
if (rest == 0) {
ans = cur == aim ? 1 : 0;
dp[cur][rest] = ans;
return ans;
}
if (cur == 1){
ans = proccess2(cur+1,rest-1,aim,n,dp);
}else if (cur == n){
ans = proccess2(cur-1,rest-1,aim,n,dp);
}else {
ans = proccess2(cur-1,rest-1,aim,n,dp) + proccess2(cur+1,rest-1,aim,n,dp);
}
dp[cur][rest] = ans;
return ans;
}
public static void main(String[] args){
System.out.println(ways1(5,2,4,6));
System.out.println(ways2(5,2,4,6));
}
}
题目二

public class CardsInLine{
//给定一个整型数组arr,代表数值不同的纸牌排成一条线
//玩家A和玩家B依次拿走每张纸牌
//规定玩家A先拿,玩家B后拿
//但是每个玩家每次只能拿走最左或最右的纸牌
//玩家A和玩家B都绝顶聪明
//请返回最后获胜者的分数。
public static int win1(int[] arr){
if (arr == null || arr.length == 0){
return 0;
}
int first = f1(arr,0,arr.length-1);
int second = s1(arr,0,arr.length-1);
return Math.max(first,second);
}
private static int s1(int[] arr,int L,int R){
if (L == R){
return arr[L];
}
int p1 = f1(arr,L+1,R);
int p2 = f1(arr,L,R-1);
return Math.min(p1,p2);
}
private static int f1(int[] arr,int L,int R){
if (L == R){
return arr[L];
}
int p1 = arr[L]+s1(arr,L+1,R);
int p2 = arr[R] + s1(arr,L,R-1);
return Math.max(p1,p2);
}
public static int win2(int[] arr){
if (arr == null || arr.length == 0){
return 0;
}
int N = arr.length;
int[][] fmap = new int[N][N];
int[][] gmap = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
fmap[i][j] = -1;
gmap[i][j] = -1;
}
}
int first = f2(arr,0,arr.length-1,fmap,gmap);
int second = g2(arr,0,arr.length-1,fmap,gmap);
return Math.max(first,second);
}
private static int f2(int[] arr,int L,int R,int[][] fmap,int[][] gmap){
if (fmap[L][R] != -1){
return fmap[L][R];
}
int ans = 0;
if (L == R){
ans = arr[L];
}else {
int p1 = arr[L]+g2(arr,L+1,R,fmap,gmap);
int p2 = arr[R] + g2(arr,L,R-1,fmap,gmap);
ans = Math.max(p1,p2);
}
fmap[L][R] = ans;
return ans;
}
private static int g2(int[] arr,int L,int R,int[][] fmap,int[][] gmap){
if (gmap[L][R] != -1){
return gmap[L][R];
}
int ans = 0;
if (L != R){
int p1 = f2(arr,L+1,R,fmap,gmap);
int p2 = f2(arr,L,R-1,fmap,gmap);
ans = Math.min(p1,p2);
}
gmap[L][R] = ans;
return ans;
}
public static void main(String[] args){
int[] arr = { 5, 7, 4, 5, 8, 1, 6, 0, 3, 4, 6, 1, 7 };
System.out.println(win1(arr));
System.out.println(win2(arr));
}
}
题目三

public class Knapsack{
// 所有的货,重量和价值,都在w和v数组里
// 为了方便,其中没有负数
// bag背包容量,不能超过这个载重
// 返回:不超重的情况下,能够得到的最大价值
public static int maxValue(int[] w,int[] v,int bag){
if (w == null || w.length == 0 || v==null || v.length != w.length || bag == 0){
return 0;
}
return proccess1(w,v,0,bag);
}
private static int proccess1(int[] w,int[] v,int index,int rest){
if (rest<0){
return -1;
}
if (index == w.length){
return 0;
}
int ans = proccess1(w,v,index+1,rest-w[index]);
if (ans != -1){
ans += v[index];
}
int ans2 = proccess1(w,v,index+1,rest);
return Math.max(ans,ans2);
}
public static int dp(int[] w,int[] v,int bag){
if (w == null || w.length == 0 || v==null || v.length != w.length || bag == 0){
return 0;
}
int N = w.length;
int[][] dp = new int[N+1][bag+1];
for(int index = N-1;index >=0; index--){
for (int rest = 0; rest <= bag;rest++){
int p1 = dp[index+1][rest];
int p2 = 0;
int next = rest - w[index] < 0 ? -1 :dp[index+1][rest-w[index]];
if (next != -1){
p2 = next+v[index];
}
dp[index][rest] = Math.max(p1,p2);
}
}
return dp[0][bag];
}
public static void main(String[] args){
int[] weights = { 3, 2, 4, 7, 3, 1, 7 };
int[] values = { 5, 6, 3, 19, 12, 4, 2 };
int bag = 15;
System.out.println(maxValue(weights, values, bag));
System.out.println(dp(weights,values,bag));
}
}
题目四

public class ConvertToLetterString{
//规定1和A对应、2和B对应、3和C对应...26和Z对应
//那么一个数字字符串比如"111”就可以转化为:
//"AAA"、"KA"和"AK"
//给定一个只有数字字符组成的字符串str,返回有多少种转化结果
public static int number(String str){
if (str == null || str.length() == 0){
return 0;
}
return process(str.toCharArray(),0);
}
private static int process(char[] str,int i){
if (str.length == i){
return 1;
}
if (str[i] == '0'){ //没有0对应的字母,说明前一个决定有问题
return 0;
}
int ways = process(str,i+1);
if (i+1 < str.length && (str[i] - '0')*10 + (str[i+1] - '0') < 27){
ways += process(str,i+2);
}
return ways;
}
// 从右往左的动态规划
// 就是上面方法的动态规划版本
// dp[i]表示:str[i...]有多少种转化方式
public static int dp1(String s){
if (s == null || s.length() == 0){
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
if (str[0] == '0'){
return 0;
}
int[] dp = new int[N+1];
dp[N] = 1;
for (int i = N-1; i >=0 ; i--) {
if (str[i] != '0') {
int ways = dp[i + 1];
if (i + 1 < str.length && (str[i] - '0') * 10 + (str[i + 1] - '0') < 27) {
ways +=dp[i+2];
}
dp[i] = ways;
}
}
return dp[0];
}
// 从左往右的动态规划
// dp[i]表示:str[0...i]有多少种转化方式
public static int dp2(String s){
if (s == null || s .length() == 0){
return 0;
}
char[] str = s.toCharArray();
int N = str.length;
if (str[0] == '0'){
return 0;
}
int[] dp = new int[N];
dp[0] = 1;
for (int i =1; i < N; i++){
if (str[i] == '0'){
if (str[i-1] == '0' || str[i-1] > '2' || (i-2 >=0 && dp[i-2] ==0)){
return 0;
}else {
dp[i] = i-2 >= 0 ? dp[i-2] : 1;
}
}else {
dp[i] = dp[i-1];
if (str[i-1] != '0' && (str[i-1] - '0')*10 + (str[i] - '0') < 27){
dp[i] += i-2 >= 0 ? dp[i-2] : 1;
}
}
}
return dp[N-1];
}
// for test
public static String randomString(int len) {
char[] str = new char[len];
for (int i = 0; i < len; i++) {
str[i] = (char) ((int) (Math.random() * 10) + '0');
}
return String.valueOf(str);
}
//for test
public static void main(String[] args) {
int N = 30;
int testTime = 1000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int len = (int) (Math.random() * N);
String s = randomString(len);
int ans0 = number(s);
int ans1 = dp1(s);
int ans2 = dp2(s);
if (ans0 != ans1 || ans0 != ans2) {
System.out.println(s);
System.out.println(ans0);
System.out.println(ans1);
System.out.println(ans2);
System.out.println("Oops!");
break;
}
}
System.out.println("测试结束");
}
}
题目五

package com.msb.test12;
public class LongestCommonSubsequence{
//给定两个字符串str1和str2,
//返回这两个字符串的最长公共子序列长度
//比如 : str1 = “a12b3c456d”,str2 = “1ef23ghi4j56k”
//最长公共子序列是“123456”,所以返回长度6
public static int longestCommonSubsequence(String s1,String s2){
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0){
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
return process(str1,str2,str1.length-1,str2.length-1);
}
private static int process(char[] str1,char[] str2,int i,int j){
if (i == 0 && j == 0){
return str1[i] == str2[j] ? 1 : 0;
}else if (i == 0){
if (str1[i] == str2[j]){
return 1;
}else {
return process(str1,str2,i,j-1);
}
}else if (j == 0){
if (str1[i] == str2[j]){
return 1;
}else {
return process(str1,str2,i-1,j);
}
}else {
int p1 = process(str1,str2,i-1,j);
int p2 = process(str1,str2,i,j-1);
int p3 = str1[i] == str2[j] ? 1+process(str1,str2,i-1,j-1) :process(str1,str2,i-1,j-1);
return Math.max(p1,Math.max(p2,p3));
}
}
public static int dp1(String s1,String s2){
if (s1 == null || s2 == null || s1.length() == 0 || s2.length() == 0){
return 0;
}
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
int M = str1.length;
int N = str2.length;
int[][] dp = new int[M][N];
dp[0][0] = str1[0] == str2[0] ? 1 :0;
for (int i = 1; i < M; i++) {
if (str1[i] == str2[0]){
dp[i][0] =1;
}else {
dp[i][0] = dp[i-1][0];
}
}
for (int j = 1; j <N ; j++) {
if (str1[0] == str2[j]){
dp[0][j] = 1;
}else {
dp[0][j] = dp[0][j-1];
}
}
for (int i = 1; i < M; i++) {
for (int j = 1; j < N; j++) {
dp[i][j] = str1[i] == str2[j] ? 1+dp[i-1][j-1] : Math.max(dp[i-1][j],dp[i][j-1]);
}
}
return dp[M-1][N-1];
}
}
----from左程云算法基础课
本文通过四个示例展示了暴力递归到动态规划的转换过程,包括机器人行走、纸牌游戏、背包问题和数字字符串转换问题。每个问题首先使用暴力递归解决,然后通过记忆化搜索实现动态规划优化,减少重复计算,提高效率。同时,探讨了动态规划的空间压缩、状态化简和四边形不等式等进一步优化技巧。
&spm=1001.2101.3001.5002&articleId=125488163&d=1&t=3&u=4fa3c29cef50465cb522cc3ee2b5046f)
2427

被折叠的 条评论
为什么被折叠?



