动态规划初步-数字三角形

问题描述:

有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方各有一个数。从第一行的数开始,每次可以往左下或右下走一格,直到走到最下行,把沿途经过的数全部加起来,如何走才能使得这个和尽量大

若熟悉回溯法,会发现这是个动态的决策问题:每次有两种选择—左下或右下。若用回溯法求出所有可能的路线,便可从中找出最优路线,但这样做的效率很低:一个n层数字三角形的完整路线有2^n-1条,当n很大时回溯法的

速度将让人无法忍受

具体思路:将当前位置(i,j)看成一个状态,然后定义状态(i,j)的指标函数d(i,j)为从格子(i,j)出发时能得到的最大和(包括格子(i,j)本身),在这个状态定义下,原问题的解是d(1,1)

那么,从格子(i,j)出发有两种决策,如果往左走,最好情况是(i,j)格子中的值与"从(i+1,j)出发的最大总和"之和,同理,如果往右走,最好情况是(i,j)格子中的值与“从(i+1,j+1)出发的最大总和”之和

    

相应状态转移方程:d(i,j)=a(i,j)+max( d(i+1,j) , d(i+1,j+1) )

方法一:(递归计算)(代码中的n为三角形层数,二维数组a中保存了三角形各个位置的数字)


    int solve(int i,int j){  
        return a[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));  
    }  

该方法可行但时间效率太低,原因是相同的子问题会被重复计算多次

方法二:递推计算(需注意边界处理)

    int i,j;  
    for(i=1;i<=n;i++){  
        d[n][i]=a[n][i];//将三角形最后一层的数据保存下来  
    }  
    for(i=n-1;i>=1;i--){  
        for(j=1;j<=i;j++){  
             d[i][j]=a[i][j]+max(d[i+1][j],d[i+1][j+1];//从倒数第一层开始计算每个状态的最大和  
        }  
    }  

方法三:记忆化搜索

将计算结果保存在数组d中,题目已知三角形中每个数均非负,故先将d中所有元素初始化为-1,若已计算过某个d[i][j],则它应是非负的,可以保证每个结点只访问一次。

    int solve(int i,int j){  
        if(d[i][j]>=0)return d[i][j];  
        return d[i][j]=a[i][j]+( i == n ? 0 : max( solve( i+1,j ),solve( i+1,j+1 ) ) );      
    }  

最终代码:

    //方法一  
    #include<iostream>  
    #include<algorithm>  
    using namespace std;  
    const int maxn=105;  
    int array[maxn][maxn];  
    int d[maxn][maxn];   
    int n;  
    int main(){  
        int i,j;  
        cin>>n;  
        for(i=1;i<=n;i++){//读入三角形   
            for(j=1;j<=i;j++){  
                cin>>array[i][j];  
            }  
        }  
        for(i=1;i<=n;i++){//将三角形最后一行的数据保存下来,最后一行有n个元素,故从1到n遍历   
            d[n][i]=array[n][i];  
        }  
        for(i=n-1;i>=1;i--){//从倒数第二行开始计算当前最大值   
            for(j=1;j<=i;j++){  
                d[i][j]=array[i][j]+max(d[i+1][j],d[i+1][j+1]);  
            }  
        }  
        cout<<d[1][1];  
        return 0;  
    }  
    //方法二  
    #include<iostream>  
    #include<string.h>  
    using namespace std;  
    const int maxn=105;  
    int array[maxn][maxn];  
    int d[maxn][maxn];  
    int n;  
    int solve(int i,int j){  
        if(d[i][j]>=0)return d[i][j];//若d[i][j]已经计算,则直接返回   
        return d[i][j]=array[i][j]+(i==n?0:max(solve(i+1,j),solve(i+1,j+1)));//返回从当前位置出发最大的和   
    }  
    int main(){  
        int i,j;  
        memset(d,-1,sizeof(d));   
        cin>>n;//输入三角形层数   
        for(i=1;i<=n;i++){//注意第i行有i个元素   
            for(j=1;j<=i;j++){  
                cin>>array[i][j];  
            }  
        }  
        solve(1,1);  
        cout<<d[1][1]<<endl;  
        return 0;  
    }  


猜你喜欢

转载自blog.csdn.net/yx970326/article/details/79942240