一个例子来了解动态规划
用“斐波那契数”这个题来了解什么是动态规划
代码实现:
public class t {
public static void main(String[] args) {
int j = f(9);
System.out.println(j);
}
public static int f(int n){
if(n ==1){
return 1;
}
if(n==2){
return 1;
}
int c = f(n-1)+f(n-2);
return c;
}
}
这里是用递归来实现的;
接下来我们来分析一下每次计算一个数的是由有没有重复计算的过程
优化版本
import java.util.HashMap;
public class t {
public static void main(String[] args) {
HashMap<Integer,Integer> aa = new HashMap<>();
int j = f(7,aa);
System.out.println(j);
}
public static int f(int n,HashMap<Integer,Integer> a1){
if(a1.get(n)!=null){
return a1.get(n);
}
if(n ==1){
a1.put(1,1);
return 1;
}
if(n==2){
a1.put(2,1);
return 1;
}
int c = f(n-1,a1)+f(n-2,a1);
a1.put(n,c);
return c;
}
}
进阶优化
我们可以用数组来搞定这个问题
代码:
public static int dd(int a){
if(a==1||a==2){
return 1;
}
int[] arr = new int[a+1];
arr[1] = 1;
arr[2] = 1;
for (int i = 3;i<arr.length;i++){
arr[i] = arr[i-1]+arr[i-2];
}
return arr[a];
}
这就是最终的动态规划
相关算法题
1.机器人走路
我们就根据题意来完成递归代码
public class t {
public static void main(String[] args) {
int a1 = way(5,13,10,100);
System.out.println(a1);
}
public static int way(int star,int get,int ret,int N){
if(ret==0){
return star==get?1:0;
}
if(star==1){
return way(star+1,get,ret-1,N);
}
if(star==N){
return way(star-1,get,ret-1,N);
}
int a3 = way(star+1,get,ret-1,N);
int a4 = way(star-1,get,ret-1,N);
return a3+a4;
}
}
这个就是递归版本的解法
同样的我们来进行优化
这次我们直接把它改成用数组来计算,可以看到每次传入递归函数的变量有两个,因此我们可以创建一个二维数组来记录
public static int dp(int star,int get,int ret,int N){
int[][] arr = new int[N+1][ret+1];
arr[get][0] = 1;
for(int i = 1;i<ret+1;i++){
for(int j = 1;j<N+1;j++){
if(j==1){
arr[j][i] =arr[j+1][i-1];
continue;
}
if(j==N){
arr[j][i] =arr[j-1][i-1];
continue;
}
arr[j][i] = arr[j+1][i-1]+arr[j-1][i-1];
}
}
return arr[star][ret];
}
这样我们就完成了
2.先手后手抽牌
递归代码:
public class t {
public static void main(String[] args) {
int[] arr = {20,100,3,2};
int a = xian(arr,0,3);
System.out.println(a);
}
public static int xian(int[] arr,int l,int r){
if(l==r){
return arr[l];
}
int a1 = arr[l]+hou(arr,l+1,r);
int a2 = arr[r] +hou(arr,l,r-1);
return Math.max(a1,a2);
}
public static int hou(int[]arr,int l,int r){
if(l==r){
return arr[l];
}
int a1 = xian(arr,l+1,r);
int a2 = xian(arr,l,r-1);
return Math.min(a1,a2);
}
}
经过分析后可以知道,这个递归函数有重复计算过程因此我们可以进行优化
优化后代码:
public class t {
public static void main(String[] args) {
int[] arr = {20,100,3,2};
System.out.println(qq(arr));
}
public static int qq(int[] arr){
int[][] xian = new int[arr.length][arr.length];
int[][] hou = new int[arr.length][arr.length];
for (int i = 0;i<arr.length;i++){
for (int k =0;k<arr.length;k++){
xian[i][k] = -1;
hou[i][k] = -1;
}
}
return xian(arr,0,arr.length-1,xian,hou);
}
public static int xian(int[] arr,int l,int r,int[][] xain,int[][]hou){
if(xain[l][r]!=-1){
return xain[l][r];
}
if(l==r){
xain[l][l] = arr[l];
return arr[l];
}
int a1 = arr[l]+hou(arr,l+1,r,xain,hou);
int a2 = arr[r] +hou(arr,l,r-1,xain,hou);
int c = Math.max(a1,a2);
xain[l][r] = c;
return c;
}
public static int hou(int[]arr,int l,int r,int[][] xain,int[][] hou ){
if(hou[l][r]!=-1){
return hou[l][r];
}
if(l==r){
return 0;
}
int a1 = xian(arr,l+1,r,xain,hou);
int a2 = xian(arr,l,r-1,xain,hou);
int c = Math.min(a1,a2);
hou[l][r] = c;
return c;
}
}
同样是建立表去记录已将算过的值
接下来直接用两个二维数组来代替这个递归
public static int gg(int[] arr){
int[][] xian = new int[arr.length][arr.length];
int[][] hou = new int[arr.length][arr.length];
for (int i = 0;i<arr.length;i++){
xian[i][i] = arr[i];
}
int N=arr.length;
for (int startCol = 1; startCol < N; startCol++) {
int L = 0;
int R = startCol;
while (R < N) {
int i = L;
int k = R;
int a1 = arr[i] + hou[i + 1][k];
int a2 = arr[k] + hou[i][k - 1];
xian[i][k] = Math.max(a1, a2);
int a11 = xian[i][k - 1];
int a22 = xian[i + 1][k];
hou[i][k] = Math.min(a11, a22);
L++;
R++;
}
}
return Math.max(xian[0][arr.length-1],hou[0][arr.length-1]);
}
3.背包问题
题意描述:
给定两个数组一个数组记录物品的价值,一个数组记录物品的重量,数组的下标代表物品的编号,再给定一个背包能装下的最大重量,求出可以取得的最大价值数
这道题我们可以直接进行暴力递归来算,就是把每一种情况的算出来然后找出最大值
递归代码展示:
public class t {
public static void main(String[] args) {
int[] a1 = {12,3,100,12,46,78};
int[] a2 = {2,4,5,2,63,4};
System.out.println(jia(a1, a2, 10, 0));
}
public static int jia(int[] v,int[] b,int bag ,int i){
if(bag==0||i==v.length){
return 0;
}
int a1 = 0;
if(bag-b[i]>=0) {
a1 = v[i] + jia(v, b, bag - b[i], i + 1);
}
int a2 = jia(v,b,bag,i+1);
return Math.max(a1,a2);
}
}
这个同样也可以进行优化:
public static int biao(int[] v,int[] b,int bag ){
int[][] aa = new int[v.length][bag+1];
for (int i =v.length-1;i>=0;i--){
for(int k = 1;k<=bag;k++){
int a1 = 0;
int a2 = 0;
if(k-b[i]>=0) {
a1 = v[i] + jia(v, b, bag - b[i], i + 1);
}
a2 = jia(v,b,bag,i+1);
aa[i][k] = Math.max(a1,a2);
}
}
return aa[0][bag];
}
3.字符串转化
递归代码展示:
public class t {
public static void main(String[] args) {
String aa = "305";
char[] a =aa.toCharArray();
System.out.println(zu(a, 0));
}
public static int zu(char[] arr,int i){
if(i==arr.length){
return 1;
}
if(arr[i]=='0'){
return 0;
}
int a1 =zu(arr,i+1);
if(i+1!=arr.length&&(arr[i]-'0')*10+(arr[i+1]-'0')<27){
a1 +=zu(arr,i+2);
}
return a1;
}
}
优化代码:
public static int biao(char[] arr){
int[] aa = new int[arr.length+1];
for(int i = arr.length;i>=0;i--){
if(i==arr.length){
aa[i]=1;
continue;
}
if(arr[i]=='0'){
aa[i] = 0;
continue;
}
int a1 =aa[i+1];
if(i+1!=arr.length&&(arr[i]-'0')*10+(arr[i+1]-'0')<27){
a1 +=aa[i+2];
}
aa[i] = a1;
}
return aa[0];
}