一个简单的动态规划例子

动态规划 既Dynamic Programming
1)动态规划是运筹学中用于求解决策过程中的最优化数学方法。 当然,我们在这里关注的是作为一种算法设计技术,作为一种使用多阶段决策过程最优的通用方法。
它是应用数学中用于解决某类最优化问题的重要工具。

2)如果问题是由交叠的子问题所构成,我们就可以用动态规划技术来解决它,一般来说,这样的子问题出现在对给定问题求解的递推关系中,这个递推关系包含了相
同问题的更小子问题的解。动态规划法建议,与其对交叠子问题一次又一次的求解,不如把每个较小子问题只求解一次并把结果记录在表中(动态规划也是空间换时间
的),这样就可以从表中得到原始问题的解。

直接看例子

国际象棋中的车可以水平的或竖直的移动,一个车要从一个棋盘的一角移到对角线的另一角,有多少种最短路径?

用动态规划算法求解(假设棋盘为n*n,最短路即不能回退)

我们设定 F[i , j]表示从坐标(0,0)到(i ,j)的方法数(定义F[i , j]的含义,动态规划的一个关键点):

很明显,要走到坐标(i ,j),它的上一步的坐标必定是(i-1 ,j)或者(i ,j-1) (分析动态规划问题的逆向思考)

这样就将问题描述为了交叠子问题:

F[i , j] = F[i -1, j] + F[i , j-1] ( F[i , j] 拆解成更小的交叠的子问题 )

我们要求的是F[n , n]

列出初始边界条件:

F[0 , j] = 1
F[i , 0] = 1

填矩阵的形式:可以按行也可以按列。

典型的动态规划的算法,由最小的边界条件处依次递归.
把矩阵填出来以后也很简单
1  8  36 120 330  792 1716  3432

  1  7  28 84  210  462 924   1716

  1  6  21 56  126  252  462   792

  1   5  15 35  70   126   210  330

  1  4  10  20  35  56    84   120

  1   3  6  10  15   21    28    36

  1   2   3   4   5   6     7     8

  车  1   1   1   1   1     1     1

这个图似曾相识吧,没错,这就是帕斯卡三角形(Pascal's Triangle)

   1

  11

  121

  1331

  14641

  ...........


的变形.即二项式的展开系数.因此其实F[n , n]即为C(2n , n)
(c(n,m)=p(n,m)/m!=n!/((n-m)!*m!))

二项式系数的性质图里也已经很明显了 C(n,k)=C(n-1,k-1)+C(n-1,k) n>k>0


代码:

/**
  * Copyright 2012, NEWTOUCH Co., Ltd.  All right reserved.
  */
 package calculation.dynamicprogramming;
 
 /**
  * @author aijnec
  */
 public class ChessTest {
 
     //棋盘维度
     private static int n = 8;
     //棋盘模型
     private long[][] chessBoard = new long[n][n];
 
     /**
      * 构造棋盘矩阵 往格子里填方法数
      *
      */
     private void buildChessBoard(){            
 
         for(int i=0;i<n;i++){            
 
             for(int j=0;j<n;j++){                
 
                 if(i==0 || j==0){
                     //边界条件
                     chessBoard[i][j]=1;
                     //System.out.println("chessBoard["+i+"]"+"["+j+"]="+chessBoard[i][j]);
                 }else{
                     //子方法递推
                     chessBoard[i][j]=chessBoard[i-1][j]+chessBoard[i][j-1];
                     //System.out.println("chessBoard["+i+"]"+"["+j+"]="+chessBoard[i][j]);
                 }
             }
         }
     }
 
     /**
      * 取得从(0,0)走到对角端点(n,n)的最短方法数
      * @return
      */
     public void getPointNum(){        
         buildChessBoard();
         System.out.println("pointNum="+chessBoard[n-1][n-1]);
     }
 
     public static void main(String[] args){
         ChessTest test = new ChessTest();
 
         long startTick,endTick;
         startTick = System.nanoTime();
 
         test.getPointNum();
 
         endTick = System.nanoTime();
         System.out.println("process  costs "+(endTick-startTick)+" ns");
 
     }
 }
 

猜你喜欢

转载自aijnecjay.iteye.com/blog/1681380