动态规划

例题一 数字三角形

7
3   8
8   1   0
2   7   4   4
4   5   2   6   5

在上面的数字三角形中寻找一条从顶部到底边的路径,使得 路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。

三角形的行数大于1小于等于100,数字为 0 - 99。

使用递推方法

使用递推从底部开始,依次判断记录,直到顶部

题目链接 http://poj.org/problem?id=1163

代码

#include<iostream>
#include<algorithm>
using namespace std;
int s[105][105],maxn[105][105];
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
            cin>>s[i][j];
    for(int i=1;i<=n;i++)
        maxn[n][i]=s[n][i];
    for(int i=n-1;i>=1;i--)
    for(int j=1;j<=i;j++){
        maxn[i][j]=max(maxn[i+1][j],maxn[i+1][j+1])+s[i][j];
    }
    cout<<maxn[1][1]<<endl;
    }
    return 0;
}

 因为记录数组只需要记录一行的数据所有可以对maxn进行空间优化

#include<iostream>
#include<algorithm>
using namespace std;
int s[105][105],maxn[105];
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
            cin>>s[i][j];
    for(int i=1;i<=n;i++)
        maxn[i]=s[n][i];
    for(int i=n-1;i>=1;i--)
    for(int j=1;j<=i;j++){
        maxn[j]=max(maxn[j],maxn[j+1])+s[i][j];
    }
    cout<<maxn[1]<<endl;
    }
    return 0;
}

因为maxn每次记录一行之后,就会动态的修改一行,而且判断过后数据就不使用了,所有还可以直接记录在s里,在进行空间优化

#include<iostream>
#include<algorithm>
using namespace std;
int s[105][105];
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
            cin>>s[i][j];
    for(int i=n-1;i>=1;i--)
    for(int j=1;j<=i;j++){
        s[i][j]=max(s[i+1][j],s[i+1][j+1])+s[i][j];
    }
    cout<<s[1][1]<<endl;
    }
    return 0;
}

解决动态规划的一般步骤

1> 将问题转化为若干个子问题。这些子问题和原问题的形式相同或类似,通过解决这类子问题,从而解决最终的问题。

子问题只需要计算一次。

2> 确定状态。所有“状态”的集合,构成问题的“状态空间”。“状态 空间”的大小,与用动态规划解决问题的时间复杂度直接相关。 在数字三角形的例子里,一共有N×(N+1)/2个数字,所以这个 问题的状态空间里一共就有N×(N+1)/2个状态。整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。   

3> 确定边界的值

4> 确定状态转移方程。定义出什么是“状态”,以及在该 “状态”下的“值”后,就要 找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(“人人为我”递推型)。状 态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。

能用动态规划解决问题所具有的特点

1> 问题具有最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构性质。

2> 无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。 

例题二 

一个数的序列 bi,当 b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列( a1a2, ..., aN),我们可以得到一些上升的子序列( ai1ai2, ..., aiK),这里1 <= i1i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8). 

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

Input

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

Output

最长上升子序列的长度。

Sample Input

7
1 7 3 5 9 4 8

Sample Output

4

题目链接 http://bailian.openjudge.cn/practice/2757?lang=en_US

人人为我型代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int s[1005],maxn[1005];
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>s[i];
            maxn[i]=1;
        }
        for(int i=2;i<=n;i++)
        for(int j=1;j<i;j++){
            if(s[i]>s[j])
                maxn[i]=max(maxn[i],maxn[j]+1);
        }
        cout<<*max_element(maxn+1,maxn+n+1)<<endl; //输出maxn+1到maxn+n+1闭区间中的最大值
    }
    return 0;
}

 

 

我为人人型代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int s[1005],maxn[1005];
int main()
{
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>s[i];
            maxn[i]=1;
        }
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
        if(s[j]>s[i]){
            maxn[j]=max(maxn[j],maxn[i]+1);
        }
        cout<<*max_element(maxn+1,maxn+n+1)<<endl;
    }
    return 0;
}

动态规划的三种形式

1> 记忆递归型

优点:只经过有用的状态,没有浪费。递推型会查看一些 没用的状态,有浪费。

缺点:可能会因递归层数太深导致爆栈,函数调用带来额 外时间开销。无法使用滚动数组节省空间。总体来说,比递推型慢。

2>  "我为人人"递推型

没有什么明显的优势,有时比较符合思考的习惯。个别特殊题目中会比“人人为我”型节省空间。

3> "人人为我"递推型

状态i的值Fi由若干个值 已知的状态值Fk,Fm,..Fy 推出,如求和,取最大值 …… 在选取最优备选状态的值Fm,Fn,…Fy时,有可能有好的算法或数据结构可以用来显著降低时间复杂度。

例题三 最长公共子序列 

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, ..., xm > another sequence Z = < z1, z2, ..., zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, ..., ik > of indices of X such that for all j = 1,2,...,k, x ij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.

Input

The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.

Output

For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.

Sample Input

abcfbc         abfcab
programming    contest 
abcd           mnp

Sample Output

4
2
0

题目链接 http://poj.org/problem?id=1458

人人为我代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
char s1[1005],s2[1005];
int maxn[1005][1005];
int main()
{
    while(cin>>s1>>s2){
        int len1=strlen(s1);
        int len2=strlen(s2);
        for(int i=0;i<=len1;i++)
            maxn[i][0]=0;
        for(int i=0;i<=len2;i++)
            maxn[0][i]=0;
        for(int i=1;i<=len1;i++)
        for(int j=1;j<=len2;j++){
            if(s1[i-1]==s2[j-1])
                maxn[i][j]=maxn[i-1][j-1]+1;
            else
                maxn[i][j]=max(maxn[i-1][j],maxn[i][j-1]);
        }
        cout<<maxn[len1][len2]<<endl;
    }
    return 0;
}

例题四 最佳加法表达式

有一个由1..9组成的数字串.问如果将m个加 号插入到这个数字串中,在各种可能形成的 表达式中,值最小的那个表达式的值是多少

思路 假定数字串长度是n,添完加号后,表达式的最后 一个加号添加在第  i 个数字后面,那么整个表达 式的最小值,就等于在前 i 个数字中插入 m – 1 个加号所能形成的最小值,加上第 i + 1到第 n 个数字所组成的数的值(i从1开始算。

所以就能写出如下的递推方程

if m==0

    V(m,n)=n个数字构成的整数

else if n<m+1

   V(m,n)=无穷

else

   V(m,n)=Min(V(m-1,i)+Num(i+1,n))(i=m.....n-1)

题目链接 http://bailian.openjudge.cn/practice/4152?lang=en_US

int minx(int m,int len) // m表示有多少个加号,len待处理数字的长度
{
    if(m==0)
        return num[1][len];
    int minn=90000000;
    for(int i=m;i<len;i++){
        int k=minx(m-1,i)+num[i+1][len]; // num[a][b]是a到b的整体数字
        if(minn>k)
            minn=k;
    }
    return minn;
}

例题五 神奇的口袋

题目链接 http://bailian.openjudge.cn/practice/2755?lang=en_US

有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a 1,a 2……a n。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。

Input

输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a 1,a 2……a n的值。

Output

输出不同的选择物品的方式的数目。

Sample Input

3
20
20
20
Sample Output

3
代码(动态规划版)

#include<iostream>
#include<cstring>
using namespace std;
int n,a[40],maxn[40][40];
int main()
{
    while(cin>>n){
        memset(maxn,0,sizeof(maxn));
        for(int i=1;i<=n;i++){
            cin>>a[i];
            maxn[0][i]=1;
        }
       maxn[0][0]=1;
       for(int i=1;i<=40;i++)
       for(int j=1;j<=n;j++){
         maxn[i][j]=maxn[i][j-1];
         if(i-a[j]>=0){
            maxn[i][j]+=maxn[i-a[j]][j-1];
         }
       }
       cout<<maxn[40][n]<<endl;
    }
    return 0;
}

代码(递归版)

#include<iostream>
using namespace std;
int n,a[40];
int way(int m,int n)
{
    if(m==0)
        return 1;
    if(n<=0)
        return 0;
    return way(m,n-1)+way(m-a[n],n-1);
}
int main()
{
    while(cin>>n){
        for(int i=1;i<=n;i++)
            cin>>a[i];
        cout<<way(40,n)<<endl;
    }
    return 0;
}

例题六 0-1背包问题

题目链接 http://poj.org/problem?id=3624

Description

Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a 'desirability' factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).

Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.

Input

* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di

Output

* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

Sample Input

4 6
1 4
2 6
3 12
2 7

Sample Output

23

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int s1[20000],s2[20000],dp[20000];
int main()
{
    int n,m;
    while(cin>>n>>m){
        for(int i=1;i<=n;i++)
            scanf("%d%d",&s1[i],&s2[i]);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
        for(int j=m;j>=s1[i];j--){
            dp[j]=max(dp[j-s1[i]]+s2[i],dp[j]);
        }
        cout<<dp[m]<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值