【动态规划---dp经典问题总结:】

动态规划---dp经典问题总结:

1.01背包

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8

 首先,状态表示:1.集合:所有只考虑前i个物品,并且总体积不超过j的选法集合。2.属性;max。

然后,状态计算:将f(i,j)分为所有不选择第i个物品的方案和所有选第i个物品的方案,第一个方案方程为f(i-1,j):表示从前i-1个物品中选,第二个方案方程为f(i-1,j-v[i])+w[i],然后就变成了f(i,j)=max(f(i-1,j),f(i-1,j-v[i])+w[i]),最后输出f[n][m]。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N][N];
int n,m,v[N],w[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=N;++i)
    cin >> v[i] >> w[i];
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
        f[i][j]=f[i-1][j];
        if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
    }
    cout << f[n][m];
    return 0;
}

 2.完全背包

有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤10000≤1000
0<vi,wi≤10000≤1000

输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

 首先,状态表示:1.集合:和01背包一样。2.属性:max。然后,状态计算:因为是无限次用物品,所以f(i,j)可划分为多个子集合:选0个第i个物品,选1个,选2个........选k个.......    分析选0个,方程为f(i-1,j),以选k个为例,方程为f(i-1,j-k*v[i])+w[i],然后联立方程f(i,j)=max(f(i-1,j),f(i-1,j-v)+w,f(i-1,2v)+w,.......)和f(i,j-v)=max(f(i-1,j-v),f(i-1,j-2v)+w,f(i-1,j-3v)+w,........),得到f(i,j)=max(f(i-1,j),f(i,j-v)+w)。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int v[N],w[N];
int n,m;
int f[N][N];
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
    cin >> v[i] >> w[i];
    for (int i = 1; i <= n; i ++ )
    for (int j = 1; j <= m; j ++ ){
        f[i][j]=f[i-1][j];
        if(j>=v[i]){
            f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
        }
    }
    cout << f[n][m];
    return 0;
}

3.石子合并(经典区间dp)

设有 N堆石子排成一排,其编号为 1,2,3,…,N。

每堆石子有一定的质量,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有 4堆石子分别为 1 3 5 2, 我们可以先合并 1、2 堆,代价为 4,得到 4 5 2, 又合并 1、2 堆,代价为 9,得到 9 2 ,再合并得到11,总代价为 4+9+11=24;

如果第二步是先合并2、3 堆,则代价为 7,得到 4 7,最后一次合并代价为 11,总代价为 4+7+11=22。

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

输入格式

第一行一个数 N 表示石子的堆数 N。

第二行 N 个数,表示每堆石子的质量(均不超过 1000)。

输出格式

输出一个整数,表示最小代价。

数据范围

1≤n≤300

输入样例:
4
1 3 5 2
输出样例:
22

集合:所有将[i,j]合并成一堆方案的集合。属性:min。

状态计算: 将f(i,j)区间分为从i到k,k+1到j,然后合并两边(前缀和思想)s[j]-s[i-1],所以最后f(i,j)=min(f(i,j),f(i,k)+f(k+1,j)+s[j]-s[i-1])。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 310;
int n,INF=1e9;
int f[N][N];
int w[N],s[N];
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];
    for (int i = 1; i <= n; i ++ ) s[i]=s[i-1]+w[i];//前缀和
    for(int len=2;len<=n;++len)
    for(int i=1;i+len-1<=n;++i){
     int j=i+len-1;
     f[i][j]=INF;
     for(int k=i;k<j;++k){
         f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
     }
    }
    cout << f[1][n];
    return 0;
}

4.最长公共子序列

给出 1,2,…… ,n 的两个排列 A 和 B ,求它们的最长公共子序列。

输入格式

第一行是一个数 n。

接下来两行,每行为 n 个数,为自然数 1,2,…… ,n 的一个排列。

输出格式

一个数,即最长公共子序列的长度。

样例输入 

3 2 1 4 5
1 2 3 4 5

样例输出 
3

提示

- 对于 50% 的数据, n <= 10^3;
- 对于 100% 的数据, n <= 10^5。

集合:所有A[1--i]与B[1--j]的公共子序列集合。属性:max。

状态计算:分为4种(其实是3种),不含a[i],b[j],含a[i],不含b[j],不含a[i],含b[j],都包含。然后第一种可以分到第二种或第三种里面,所以是三种,首先先在中间两种中取max,第四种是需要判断是否a[i]==b[j]。

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
char a[N],b[N];
int f[N][N];
int main()
{
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
    f[i][j]=max(f[i-1][j],f[i][j-1]);
    if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
}
cout<<f[n][m];
    return 0;
}

5.采药

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。

为此,他想拜附近最有威望的医师为师。

医师为了判断他的资质,给他出了一个难题。

医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

输入文件的第一行有两个整数 T 和 M,用一个空格隔开,T 代表总共能够用来采药的时间,M代表山洞里的草药的数目。

接下来的 M 行每行包括两个在 1 到 100之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出文件包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

数据范围

1≤T≤1000
1≤M≤100

输入样例:
70 3
71 100
69 1
1 2
输出样例:
3

这道题可以看作01背包,就是将时间看作背包容量(体积),每一株草药看作物品 ,所以集合就是:只看前i种草药,且总体积不超过j的最大价值的选法。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n,m;
int f[N][N];
int v[N],w[N];
int main()
{
    cin >> m>>n;//时间和数目(体积和个数)
    for (int i = 1; i <= n; i ++ ) cin >>v[i]>>w[i];//时间和价值(体积和价值)
    for (int i = 1; i <= n; i ++ )//前i个物品
    for (int j = 1; j <= m; j ++ ){//总体积不超过j
        f[i][j]=f[i-1][j];
        if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
    }
    cout << f[n][m];
    return 0;
}

6.数字三角形

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5
输入格式

第一行包含整数 n,表示数字三角形的层数。

接下来 n 行,每行包含若干整数,其中第 i 行表示数字三角形第 i 层包含的整数。

输出格式

输出一个整数,表示最大的路径数字和。

数据范围

1≤n≤500,
−10000≤三角形中的整数≤10000

输入样例:
5
7
3 8
8 1 0 
2 7 4 4
4 5 2 6 5
输出样例:
30

经典的线性dp问题,核心是从下到上考虑,所以集合为:从底到上走到f(i,j)的所有路线集合。属性 :max。f(i,j)有两个分支,分别为从f(i+1,j)走到f(i,j)【不管前面怎么走,最后一步是从f(i+1,j)走到f(i,j)】,和f(i+1,j+1)走到f(i,j)【同理】,最后结果为f(1,1)。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
int n;
int f[N][N];
int main()
{
    cin >> n;
    for (int i=1;i<=n;++i)
    for(int j=1;j<=i;++j){
        scanf("%d",&f[i][j]);
    }
    for(int i=n-1;i>=1;--i)
    for(int j=1;j<=i;++j){
        f[i][j]+=max(f[i+1][j],f[i+1][j+1]);
    }
    cout << f[1][1];
    return 0;
}

  • 46
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
01背包问题动态规划中的一个经典问题,它的解法也非常经典,下面是我对该问题动态规划总结。 1. 状态定义 定义一个二维数组dp[i][j],其中i表示当前考虑到第i个物品,j表示当前背包容量为j,dp[i][j]表示在前i个物品中,背包容量为j时的最大价值。 2. 状态转移方程 对于每个物品,我们可以选择将其放入背包,也可以选择不放入背包,因此状态转移方程如下: 如果不将第i个物品放入背包,则 dp[i][j] = dp[i - 1][j] 即前i-1个物品已经在容量为j的背包中的最大价值就是dp[i - 1][j]。 如果将第i个物品放入背包,则 dp[i][j] = dp[i-1][j-w[i]] + v[i] 即前i-1个物品在容量为j-w[i]的背包中的最大价值加上第i个物品的价值v[i]。 最终的状态转移方程为: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]) 3. 边界条件 当物品数量为0时,dp[0][j]都等于0;当背包容量为0时,dp[i][0]都等于0。 4. 求解最优解 最终的最大价值为dp[n][W],其中n表示物品数量,W表示背包容量。 5. 代码实现 以下是01背包问题动态规划代码实现,其中w和v分别表示物品的重量和价值,n和W表示物品数量和背包容量: ```python def knapsack(w, v, n, W): dp = [[0] * (W+1) for _ in range(n+1)] for i in range(1, n+1): for j in range(1, W+1): if j < w[i-1]: dp[i][j] = dp[i-1][j] else: dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1]) return dp[n][W] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值