算法题——动态规划

例题1:数字三角形

  http://poj.org/problem?id=1163

  首先使用递归的做法:

#include<iostream>                 
using namespace std;               
                                   
#define MAX 101                    
int D[MAX][MAX]; //保存三角形           
int n;                             
                                   
int MaxSum(int i, int j) {         
    if (i==n) {                    
        return D[i][j];            
    }                              
    int x = MaxSum(i+1, j); // 左下  
    int y = MaxSum(i+1, j+1);      
                                   
    int tmp = x > y ? x : y;       
    return tmp + D[i][j];          
}                                  
                                   
int main()                         
{                                  
    int i, j;                      
    cin >> n;                      
                                   
    //输入三角形                        
    for (i = 1; i <= n; i++) {     
        for (j = 1; j <= i; j++) { 
            cin >> D[i][j];        
        }                          
    }                              
    cout << MaxSum(1,1) << endl;   
                                   
    return 0;                      
}                                  
                                   

  因为存在太多的重复计算,当数据量大的时候,就会严重超时。

  可以将每一次递归过程中,算出的值存起来,如果下次再进行计算时,如果之前已经计算过(已经被存起来了),那么就直接取存的值。

  动态规划

  状态方程:ans = max( maxSum(i+1, j),maxSum(i+1,j+1))+ a[i][j]

#include<iostream>                                           
using namespace std;                                         
                                                             
#define MAX 101                                              
int D[MAX][MAX]; //保存三角形                                     
                                                             
//用来保存每一个位置到底边路径的最大和(每次递归的结果),避免重复计算                         
int maxsum[MAX][MAX];                                        
                                                             
int n;  //行数                                                 
                                                             
int MaxSum(int i, int j) {                                   
    // 如果之前已经计算过了,那么就直接使用结果,不用再次计算                           
    if (maxsum[i][j] != -1) {                                
        return maxsum[i][j];                                 
    }                                                        
                                                             
    if (i==n) {                                              
        maxsum[i][j] = D[i][j];                              
    } else {                                                 
        int x = MaxSum(i+1, j); // 左下                        
        int y = MaxSum(i+1, j+1);                            
        int tmp = x > y ? x : y;                             
        maxsum[i][j] = tmp + D[i][j];                        
    }                                                        
                                                             
    return maxsum[i][j];                                     
}                                                            
                                                             
int main()                                                   
{                                                            
    int i, j;                                                
    cin >> n;                                                
                                                             
    //输入三角形                                                  
    for (i = 1; i <= n; i++) {                               
        for (j = 1; j <= i; j++) {                           
            cin >> D[i][j];                                  
            maxsum[i][j] = -1; // -1表示这个位置没有计算过              
        }                                                    
    }
    cout << MaxSum(1,1) << endl;
    return 0;
}

 

最长上升最序列

  http://bailian.openjudge.cn/practice/2757

#include<iostream>
#include<algorithm>
using namespace std;

#define MAXN 1010
int a[MAXN];       //保存输入的数据
int maxLen[MAXN];  //用来保存每一个位置作为终点的最长子序列长度

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin>> a[i];
        maxLen[i] = 1;  //每个位置作为终点的最长子序列长度初始为1,即自身
    }

    //求第i个数作为终点的最长子序列长度
    for (int i = 2; i <= n; i++) {
        //查看第j个数作为终点的最长子序列长度
        for (int j = 1; j < i; j++) {
            if (a[i] > a[j]) {
                maxLen[i] = max(maxLen[i], maxLen[j] + 1);
            }
        }
    }

    int res = maxLen[1];
    //依次循环遍历出每个元素作为终点的最长子序列,最大值即为整个数组的最长子序列
    for (int i = 2; i < n; i++) {
        if (res < maxLen[i]) {
            res = maxLen[i];
        }
    }
    cout<< res <<endl;
}

  

字符串中找出连续最长的数字串

读入一个字符串str,输出字符串str中的连续最长的数字串 
输入描述:
每个测试输入包含1个测试用例,一个字符串str,长度不超过255。
输出描述:
在一行内输出str中里连续最长的数字串。
输入例子1:
abcd12345ed125ss123456789
输出例子1:
123456789

  和上面一个球最长连续子串的方法类似,都是倒着来,用一个数组来记录每一个字符的最长子串,如果当前字符时数字,并且前面一个字符大1,那么,这个位置的计数就是前一个字符计数加1.

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int main()
{
    char s[300];
    int dp[300];
    while(gets(s)){
        memset(dp, 0, sizeof(dp));
        int length = strlen(s);
        int max_cnt = 0;
        int max_index = 0;
        for (int i = 1; i < length; i++) {
            //首先判断是不是数字,如果不是数字,则不作处理
            if (s[i] >= '0' && s[i] <= '9' && (s[i] - s[i-1]) == 1) {
                dp[i] = dp[i-1] + 1;
                if (dp[i] > max_cnt) {
                    max_cnt = dp[i];
                    max_index = i;
                }
            }
        }

        for (int i = max_index - max_cnt; i <= max_index; i++){
            cout << s[i];
        }
        cout << endl;
    }
    return 0;
}

  

 

 

例题3:最长公共子串

  http://poj.org/problem?id=1458

 

例题4:求出栈序列的总数

  http://www.rqnoj.cn/problem/53

  输入n,表示1,2,3...n依次入栈,出栈顺序不定,问有多少种出栈序列?

  解析:使用动态规划的思想,状态方程:

  res = func(i-1, j) + func(i+1, j-1)

  出栈序列有入栈顺序和出栈顺序决定,用i表示栈中的元素,j表示为入栈的元素。

  

#include<iostream>
using namespace std;

int dp(int i, int j) {
    if(j == 0) {  //已经没有入栈元素了
        return 1;
    }
    int ans = dp(i+1, j-1);  

    if (i > 0) {  //栈中还有元素时
        ans += dp(i-1, j);
    }
    return ans;
}
int main()
{
    int n;
    while(cin >> n) {
        cout << dp(0, n) << endl;
    }
    return 0;
}

  至于判断一个出栈系列是否合法,可以参考:https://www.cnblogs.com/-beyond/p/6113702.html

例题4:传球游戏

  http://www.rqnoj.cn/problem/487

  状态方程:x表示球在哪里,k表示剩余的传球次数

  res = func(x+1, k-1) + func(x-1, k-1),判断当k为0时,x为原始点,则计数加1。

#include<iostream>
#include<cstring>
using namespace std;

int n,m,target = 1;
int dp[31][31];

int left(int x){
    if (x == 1) {
        return n;
    } else {
        return x-1;
    }
}
int right(int x){
    if (x == n) {
        return 1;
    } else {
        return x+1;
    }
}
int f(int x, int k) {
    if (k == 0) {
        if (x == target) {
            return 1;  //最终传递到了原始点
        }
        return 0;      //最终,球没有在原始点上
    }

    if (dp[x][k] != -1) {
        return dp[x][k];
    }
    dp[x][k] = f(left(x), k - 1) + f(right(x), k - 1);
    return dp[x][k];
}
int main()
{
    while(cin >> n >> m) {
        memset(dp, -1, sizeof(dp));
        cout << f(1, m) << endl;
    }
    return 0;
}

  注意,dp数组的默认值不要总是设为0,因为有些数据可能导致无限递归。

posted @ 2018-08-16 00:58  寻觅beyond  阅读(266)  评论(0)    收藏  举报
返回顶部