动态规划

来源:互联网 发布:淘宝卖家能删除差评 编辑:程序博客网 时间:2024/06/14 17:39
参考文章:http://www.360doc.com/content/13/0601/00/8076359_289597587.shtml

     https://www.topcoder.com/community/data-science/data-science-tutorials/dynamic-programming-from-novice-to-advanced/

 

简介

动态规划简称DP,一般基于递归或一个或多个初始状态。当前问题可以由之前的已经解决的子问题推导得出。

 

入门

以硬币为例,假设有面值为V=(1,3,5)的硬币若干,给出一个值如11,求出最少可以使用这三种硬币凑足11,答案很简单为3。以下使用动态规划的思想求解:

设d[i]表示凑够i元至少需要d[i]个硬币,以下进行递推:

i=0:没有小于0的硬币,故d[0]=0

i=1:此时只有面值为1的硬币可用,故d[1]=1

i=2:此时依然只有面值为1的硬币可用,拿起面值为1的硬币,此时只要再凑足2-1的硬币即可,即d[1]的值,故d[2]=d[2-1]+1=2

i=3:此时可用的硬币为(1,3)

情况1:拿起面值为3的硬币,此时只要再凑足3-3的硬币即可,即d[0]的值,故d[3]=d[3-3]+1=1

情况2:拿起面值为1的硬币,此时只要再凑足3-1的硬币即可,即d[2]的值,故d[3]=d[3-1]+1=3

故d[3]=min{d[3-3]+1,d[3-1]+1}=2

i=4:此时可用的硬币为(1,3)

情况1:拿起面值为3的硬币,此时只要再凑足4-3的硬币即可,即d[1]的值,故d[3]=d[4-3]+1=2

情况2:拿起面值为1的硬币,此时只要再凑足4-1的硬币即可,即d[3]的值,故d[3]=d[4-1]+1=2

故d[4]=min{d[4-3]+1,d[4-1]+1}=2

总体思路为:当要求解d[i]时,先看V中可候选的元素[v1,v2,...,vi],针对每一个选取的vi,都可以得到一个子问题d[i-vi],显然这个子问题已经求解过了,因此d[i]=min{d[i-vi]+1}。

(PS: d[i-vi]+1中的加1表示选取了一个vi,因此总数等于子问题的值加1)

代码如下:

package AlgorithmsBases;public class DynamicProgramming_01 {    public static void main(String[] args) {        // TODO Auto-generated method stub        //有三种面值的硬币        int[] V = new int[] {1,3,5};        System.out.println("result:"+getBinNum(4,V));    }        //value表示总值,V表示硬币面值,返回最少需要的硬币数目    public static int getBinNum(int value,int[] V){        int[] Min = new int[value+1];        Min[0]=0;        //除了Min[0]以外,其余先设为最大值        for(int i=1;i<Min.length;i++){            Min[i]=Integer.MAX_VALUE;        }                //以下为核心代码        //Min[i]表示凑足i需要的硬币数        for(int i=0;i<=value;i++){            //V表示现有的各个面值的硬币            for(int j=0;j<V.length;j++){                System.out.println(V[j]+":"+i);                                if(V[j]<=i && Min[i-V[j]]+1<Min[i]){                    Min[i]=Min[i-V[j]]+1;                }                            }        }        return Min[value];    }}
折叠代码

初级

接下来讨论一个经典问题:找出一个序列的最长非降子序列的长度(longest non-decreasing sequence)

比如V={5,3,4,8,6,7}的非降最长子序列为{3,4,6,7},长度为4

 

设d[i]表示V中第i个元素对应的最长非降子序列的长度,求解思路如下:

i=1,V[i]=5:此时V[1]前面没有值,因此d[1]=1

i=2,V[i]=3:此时V[1:i-1]的元素中没有小于等于V[2]=3的值,因此d[2]=1

i=3,V[i]=4:此时V[1:i-1]的元素中小于等于V[3]的元素有{V[2]=3},因此d[3]=d[2]+1=2

i=4,V[i]=8:此时V[1:i-1]的元素中小于等于V[4]的元素有{V[2]=3,V[3]=4},因此d[4]=max{d[2]+1,d[3]+1}=mxa{2,3}=3

代码如下:

package AlgorithmsBases;public class DynamicProgramming_02 {    public static void main(String[] args) {        //        int[] V = new int[] {5,3,4,8,6,7};        System.out.println("result:"+getLongestNonDecreasingSequence(V));    }        //以下代码实现输入一个数组,输出最长非降子序列的长度    //比如{5,3,4,8,6,7}的非降最长子序列为{3,4,6,7},长度为4    public static int getLongestNonDecreasingSequence(int[] V){        int[] MaxLen = new int[V.length];        //MaxLen最小值为1        for(int i=0;i<MaxLen.length;i++){            MaxLen[i]=0;        }                for(int i=0;i<V.length;i++){            //以下代码求解d[i]=max{1,d[j]+1},其中j<=i,V[j]<=V[i]            int maxb=1;            for(int j=0;j<=i;j++){                if(V[j]<=V[i] && MaxLen[j]+1>=maxb) maxb=MaxLen[j]+1;            }            MaxLen[i]=maxb;        }                int max=0;        for(int k=0;k<MaxLen.length;k++){            if(max<MaxLen[k]) max=MaxLen[k];        }        printArray(MaxLen);        return max;    }    public static void printArray(int[] maxLen){        System.out.print("printArray:");        for(int i=0;i<maxLen.length;i++){            System.out.print(maxLen[i]+" ");        }        System.out.println("");    }}
折叠代码

 

中级

有一个M*N的方格,每个格子都有一定数量的苹果,用V矩阵表示,从左上角出发,每次只能右移一步或下移一步,求解能获得的最大苹果数目。

这个问题属于二维动态规划,但思想与前面的问题类似。

 

设dp[i][j]表示我们走到第i行第j列的方格时能获得的最大苹果数目,则dp[i][j] = V[i][j] + max{dp[i-1][j],dp[i][j-1]}

代码如下:

package AlgorithmsBases;public class DynamicProgramming_03 {    public static void main(String[] args) {        //        int[][] V = new int[][] {{1,2,1,1,2},{2,1,2,2,1},{1,1,2,2,1},{2,2,1,1,1},{1,1,2,1,2},{1,1,2,1,2}};        //printArray(V);        System.out.println("result:"+getLongestNonDecreasingSequence(V));    }            public static int getLongestNonDecreasingSequence(int[][] V){        int rows = V.length;        int cols = V[0].length;        int[][] dp = new int[rows][cols];                for(int i=0;i<rows;i++){            for(int j=0;j<cols;j++){                if(i>0 && j>0){                    dp[i][j]=V[i][j]+(dp[i-1][j]>dp[i][j-1]?dp[i-1][j]:dp[i][j-1]);                }                if(i>0&&j<=0){                    dp[i][j]=V[i][j]+dp[i-1][j];                }                if(i<=0&&j>0){                    dp[i][j]=V[i][j]+dp[i][j-1];                }                if(i<=0&&j<=0){                    dp[i][j]=V[i][j];                }                            }        }        int max=0;        for(int i=0;i<rows;i++){            for(int j=0;j<cols;j++){                if(max<=dp[i][j]){                    max=dp[i][j];                }            }        }        return max;    }    public static void printArray(int[] V){        System.out.print("printArray:");        for(int i=0;i<V.length;i++){            System.out.print(V[i]+" ");        }        System.out.println("");    }    public static void printArray(int[][] V){        System.out.println("printArray:");        int rows = V.length;        int cols = V[0].length;        for(int i=0;i<rows;i++){            for(int j=0;j<cols;j++){                System.out.print(V[i][j]+" ");            }            System.out.println("");                    }        System.out.println("");    }}
折叠代码

 

0 0
原创粉丝点击