1. 程式人生 > >演算法初探-動態規劃(Dynamic Programming)

演算法初探-動態規劃(Dynamic Programming)

程式設計之中接觸到很多關於演算法的知識,想來整理一番,算是對自己記憶的一個提醒

1.Coin11Problem  問題為:使用最少的三種面值為1,3,5的硬幣組合出11元。

重要的是狀態以及狀態轉移方程 d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j個硬幣的面值;

package com.njit.dynamic_programming;

/**
 * 使用最少若干的1,3,5硬幣,組合出11元
 *  
 * @author  邵鵬
 * @version  [V1.00, 2017年7月13日]
 * @see  [相關類/方法]
 * @since V1.00
 */
public class Coin11Problem
{
    public static void main(String[] args)
    {
        //狀態以及狀態轉移方程
        //d(i)     d(i)=min{d(i-vj)+1}   vj為硬幣的值
        int[] a ={1,3,5};
        int[] dp =new int[12];
        dp[0]=0;
        for(int i=1;i<=11;i++){
            dp[i]=i;
        }
        for(int i=1;i<=11;i++){
            for(int j=0;j<3;j++){
                if(i>=a[j] && dp[i-a[j]]+1<dp[i]){
                    dp[i]=dp[i-a[j]]+1;
                }
            }
        }
        System.out.println("最少需要"+dp[11]+"個硬幣可以組成11");
//        System.out.println(a[0]+" "+a[1]+" "+a[2]);
        for(int i=1;i<=11;i++){
            System.out.print(dp[i]+" ");
        }
        
    }
    
    
    
}

以上為特定11元

以下為推廣

package com.njit.dynamic_programming;

public class Coin11ProblemCommonVersion
{
    public static void main(String[] args)
    {
        //呼叫方法 在1 3 5 三個硬幣中找出最少的組合方式 組成 sum 變數為輸入的sum
        int inputCoin=11;
        int outputCoin= new Coin11ProblemCommonVersion().getMinCoin(inputCoin);
        System.out.println("當總數為 "+inputCoin+" 時,最少的硬幣數為:"+outputCoin);
        
        int[] outputCoinArray =new Coin11ProblemCommonVersion().getMinCoinArray(inputCoin);
        System.out.println("當總數為 "+inputCoin+" 時,各硬幣數為:");
        for(int i=1;i<=inputCoin;i++){
            System.out.println("dp["+i+"]="+outputCoinArray[i]);
        }
        
    }
    
    
    /** 
     * 根據輸入,動態輸出硬幣數量
     * 
     * @param sum
     * @return
     * @see [類、類#方法、類#成員]
     */
    public int getMinCoin(int sum){
        int coin=0;
        int[] a ={1,3,5};
        int[] dp = new int[sum+1];
        dp[0]=0;
        for(int i=1;i<=sum;i++){
            dp[i]=i;
        }
        for(int i=1;i<=sum;i++){
            for(int j=0;j<3;j++){
                if(i>=a[j] && dp[i-a[j]]+1<dp[i] ){
                    dp[i]=dp[i-a[j]]+1;
                }
            }
        }
        
        return dp[sum];
    }
    
    /** 
     * 輸入最後的硬幣數,返回過程中的所有最小硬幣數
     * <功能詳細描述>
     * @param sum
     * @return
     * @see [類、類#方法、類#成員]
     */
    public int[] getMinCoinArray(int sum){
        int coin=0;
        int[] a ={1,3,5};
        int[] dp = new int[sum+1];
        dp[0]=0;
        for(int i=1;i<=sum;i++){
            dp[i]=i;
        }
        for(int i=1;i<=sum;i++){
            for(int j=0;j<3;j++){
                if(i>=a[j] && dp[i-a[j]]+1<dp[i] ){
                    dp[i]=dp[i-a[j]]+1;
                }
            }
        }
        return dp;
    }
    
}

2.MathTowerProblem (數塔問題:三角形結構,算出從下到上,數字總和最大的一條路徑的和)

狀態轉移方程

f[i][j] = max(f[i+1][j], f[i+1][j+1]) + map[i][j]

        int[][] mathTower = new int[][]{{0,0,0,0,0,0},{0,7,0,0,0,0},{0,3,8,0,0,0},
            {0,8,1,0,0,0},{0,2,7,4,4,0},{0,4,5,2,6,5}};
        System.out.println("原始節點為:");
        for(int i=1;i<=5;i++){
            for(int j=1;j<=i;j++){
                System.out.print(mathTower[i][j]+" ");
            }
            System.out.println();
        }
        
        for(int i=5;i<=5;i--){
            for(int j=1;j<i;j++){
                mathTower[i-1][j]+=Math.max(mathTower[i][j], mathTower[i][j+1]);
            }
        }
        System.out.println("自底而上的最大數值路徑得到的值為: "+mathTower[1][1]);
        for(int k=1;k<=5;k++){
            for(int t=1;t<=k;t++){
                System.out.print(" "+mathTower[k][t]);
            }
            System.out.println();
        }