一、声明
上一篇详解了dp背包问题,也就是dp原理知识的介绍,可详见CSDN。
纸上得来终觉浅,绝知此事要躬行。
本篇就是配套的dp习题集,当然,有基础的完全可以直接练习。习题内容皆是各大oj上的题目,本篇则致力于编写一部习题集,实现由浅入深,一部到位。
分为三个阶段,对于每个习题,都会有详细讲解,持续更新。
二、Step one
这里首先应该是01背包、完全背包、多重背包、分组背包几个经典问题,但是在上一篇已经有过详细讲解,所以这里不再赘述,我们直接看实战题目。
P1077 [NOIP2012 普及组] 摆花
P1077 [NOIP2012 普及组] 摆花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
问题重述:容量体积m,物品1-n种,每个体积为1,每种最多取a[i]个,求方案数。
思路:看完重述,模板题目。但是注意,观察它和多重背包的不同点在哪?要求的是方案数。既然是方案数,就是固定的,你不能说最大方案数是多少,不能像求最大价值一样,取不同的物品价值就不同。物品固定,一个容量,方案数就是死的。
那我们用y总的方法分解:状态表示f[i][j]:容量为j前提下取前i个物品的方案数。状态计算: 想想台阶问题:可以一步走、可以两步走,走到当前台阶方案数=前一阶方案数+前两阶方案数。一样的道理:当前行的上一行,也就是看上一个物品,最低体积 [j-k],加k个当前物品得f[i][j]。最高体积[j],不用加的当前物品得f[i][j]。
代码(二维,可优化):
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int a[105];
ll f[105][105];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=0;i<=n;i++)f[i][0]=1;//注意初始化第一行方案数为1,因为要用到第一行
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<=a[i];k++){
if(j>=k){
f[i][j]=(f[i][j]+f[i-1][j-k])%1000007;//上一行k+1个f的和
}
}
// cout<<f[i][j]<<" ";
}
//cout<<endl;
}
cout<<f[n][m]<<endl;
return 0;
}
P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles
P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
讲解:本题很简单,之所以列出是因为相对于一些与背包问题极相似的模板题来说,可以作为一个很经典的变式开场。
状态计算
代码:
#include <bits/stdc++.h>
using namespace std;
int f[1005][1005];
int kn[1005][1005];
int main(){
int r;
cin>>r;
for(int i=1;i<=r;i++)
for(int j=1;j<=i;j++)
cin>>kn[i][j];
for(int i=1;i<=r;i++)
for(int j=1;j<=i;j++)
f[i][j]=kn[i][j]+max(f[i-1][j-1],f[i-1][j]);
int ans=0;
for(int i=1;i<=r;i++)ans=max(ans,f[r][i]);
cout<<ans;
return 0;
}
类似题目:P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)