数字三角形如果数据中有负数,则需要把边界初始化为很小的数;而且对于第一行(即第一个数),应该直接赋值,如果直接取max的话会取到负数
数字三角形的状态转移方程为:
f[i][j]=max(f[i-1][j],f[i-1][j-1]);
也可以倒序dp:
下面的题目代码注释写的很详细了: AcWing 898. 数字三角形 - AcWing(已做笔记)
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++) f[i]=1;
for(int i=1;i<=n;i++){
for(int j=1;j
贴几道题目: AcWing 895. 最长上升子序列 - AcWing (已做笔记 )
AcWing 1017. 怪盗基德的滑翔翼 - AcWing AcWing 1010. 拦截导弹 - AcWing
AcWing 1014. 登山 - AcWing AcWing 1012. 友好城市 - AcWing
AcWing 482. 合唱队形 - AcWing AcWing 1016. 最大上升子序列和 - AcWing
详细的说明在题目代码注释中
int main()
{
int n,m;
cin >> n >> m;
string a,b;
cin >> a >> b;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
f[i]=max(f[i-1][j],f[i][j-1]);
if(a[i-1]==b[j-1]) f[i]=max(f[i],f[i-1][j-1]+1);
}
}
return 0;
}
贴一道题目: AcWing 897. 最长公共子序列 - AcWing (已做笔记)
(1)01背包
题目: AcWing 2. 01背包问题 - AcWing(已做笔记)
01背包只有选和不选两种情况,并且每种物品只有1个
二维数组写法:
#include
#include
#include
using namespace std;
const int N=1010;
int v[N],w[N],f[N][N];
int main()
{
int n,m;
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=0;j<=m;j++){
if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
else f[i][j]=f[i-1][j];//必须要从上一层获取数据
}
}
cout << f[n][m];
return 0;
}
滚动数组(一维)写法:
#include
#include
#include
using namespace std;
const int N=1010;
int v[N],w[N],f[N];
int main()
{
int n,m;
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=m;j>=v[i];j--){//倒序,保证只取一次(保证前面的没有被计算过)
f[j]=max(f[j],f[j-v[i]]+w[i]);//直接覆盖即可,因为是滚动数组,保留下来的数据就是上一层的,所以无需从上一层获取数据
}
}
cout << f[m];
return 0;
}
(2)完全背包
题目: AcWing 3. 完全背包问题 - AcWing(已做笔记)
完全背包的每种物品都有无限件
滚动数组写法:
#include
#include
#include
using namespace std;
const int N=1010;
int v[N],w[N],f[N];
int main()
{
int n,m;
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=0;j<=m;j++){//正序,保证每一种物品可以取多次(保证前面的已经被计算过,可以累加)
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout << f[m];
return 0;
}
(3)多重背包
题目: AcWing 4. 多重背包问题 - AcWing(已做笔记)
朴素滚动数组写法:(转换成完全背包和01背包)
#include
#include
#include
using namespace std;
const int N=1010;
int v[N],w[N],s[N],f[N];
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++) cin >> v[i] >> w[i] >> s[i];
for(int i=1;i<=n;i++){
if(v[i]*s[i]>=m){//装不完就转换成完全背包
for(int j=v[i];j<=m;j++){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}else{//装的完就转换成01背包
for(int j=m;j>=v[i];j--){
for(int k=s[i];k>=0;k--){//遍历一下每种物品选几件(也可以不选)
if(j>=k*v[i]) f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
}
}
}
}
cout << f[m];
return 0;
}
二进制优化:
二进制优化的原理是利用小于等于当前值的 二的次方的数可以组合成任意小于等于当前值的数,然后对这些数采用01背包的策略
#include
#include
#include
using namespace std;
const int N=1010;
int v[N],w[N],f[N],cnt;//cnt表示新划分的物品的总数量
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
int a,b,c;//a表示物品的体积,b表示物品的价值,c表示物品的数量
cin >> a >> b >> c;
int k=1;
while(k<=s){//二进制优化
cnt++;
v[cnt]=k*a;
w[cnt]=k*b;
c-=k;
k*=2;
}
if(c>0){
cnt++;
v[cnt]=c*a;
w[cnt]=c*b;
}
}
for(int i=1;i<=cnt;i++){//枚举cnt件新物品(是cnt件,不是n件)
for(int j=m;j<=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout << f[m];
return 0;
}
(4)分组背包
题目: AcWing 9. 分组背包问题 - AcWing(已做笔记)
二维数组写法:
#include
#include
#include
using namespace std;
const int N=1010;
int w[N][N],v[N][N],f[N][N],s[N];
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> s[i];
for(int k=1;k<=s[i];k++) cin >> v[i][k] >> w[i][k];
}
for(int i=1;i<=n;i++){//01背包
for(int j=0;j<=m;j++){
for(int k=0;k<=s[i];k++){//从0开始,已经包含了不选的情况
if(j>=v[i][k]) f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
//else f[i][j]=f[i-1][j]; 这一行不能写,因为上面的枚举已经包含了不选的情况了
}
}
}
cout << f[n][m];
return 0;
}
滚动数组写法:
#include
#include
#include
using namespace std;
const int N=110;
int f[N],w[N],v[N];
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){//遍历n组物品
int s;
cin >> s;//读入每组物品的件数
for(int k=1;k<=s;k++) cin >> v[k] >> w[k];//根据件数读入每组物品的每一件的体积和价值,会对上一层进行覆盖
for(int j=m;j>=0;j--){//滚动数组倒序01背包
for(int k=0;k<=s;k++){//枚举一组中的每一件物品(包括不选)
if(j>=v[k]) f[j]=max(f[j],f[j-v[k]]+w[k]);
}
}
}
cout << f[m];
return 0;
}