动态规划是一个非常难啃的问题,模型就非常的多,也没有什么很固定的模板。动态规划的模型有:背包问题 线性dp 区间dp 计数类dp 数位统计类dp 状态压缩类dp 树形dp 记忆化搜索。
其中背包问题的模型就非常之多,这篇文章记录下背包的几大类问题。
一:01背包
01背包的问题模型,问题如下:
以上这是01背包的问题,我们思考的方式采用y总的分析法。即从状态表示和状态计算上看。
以上的分析法是y总的分析套路,代码如下:
//二维
#include<bits/stdc++.h>
using namespace std;
int w[1005];//存放每一个价值
int v[1005];//存放每一个物品的体积
int f[1005][1005];//存放每个状态值
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[i][j]=f[i-1][j];//不含i的时候
if(j>=v[i]) f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);//必须前面装过的还可以至少有v[i]的地方可以装,所以要判断,否则也会出现负数的情况。
}
cout<<f[n][m]<<endl;
return 0;
}
//优化成一维,优化成一维不好理解,不如二维简单明了
#include<bits/stdc++.h>
using namespace std;
int w[1005];
int v[1005];
int f[1005];
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]<<endl;
return 0;
}
二:完全背包问题
完全背包问题指的是每一件物品都可以无限选择,只要体积不超过背包的总体积。
场景题目:
同样的,我们采用了y总的分析方法。从状态表示和状态计算两个方面去分析这道题目。
#include<bits/stdc++.h>
using namespace std;
int v[1005];
int w[1005];
int f[1005][1005];
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++)
for(int k=0;k*v[i]<=j;k++)
{
f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+w[i]*k);//表达式
}
cout<<f[n][m];
return 0;
}
//优化代码,减少一层循环
#include<bits/stdc++.h>
using namespace std;
int v[1005];
int w[1005];
int f[1005][1005];
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=1;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
}
cout<<f[n][m];
return 0;
}
//完全背包的终极写法,用一维来解决问题
#include<bits/stdc++.h>
using namespace std;
int v[1005];
int w[1005];
int f[1005];
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=v[i];j<=m;j++)
{
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
cout<<f[m];
return 0;
}
三:多重背包问题
多重背包问题是指限制第i个物品的个数而不能无限制的取,我们在分析的时候依然用yxc的dp分析法。
因此,其实这个多重背包问题的朴素版写法和完全背包是差不多的,只是我们限制取的件数
代码:
//多重背包的朴素版写法
#include<bits/stdc++.h>
using namespace std;
int w[105];
int v[105];
int s[105];
int f[105][105];
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++)
for(int j=0;j<=m;j++)
for(int k=0;k*v[i]<=j&&k<=s[i];k++)//给定双重限制,这个代码只比完全背包多一点代码
{
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
}
cout<<f[n][m]<<endl;
return 0;
}
四.多重背包的优化
因为在优化之前,多重背包的时间复杂n*m*s三重循环,当数据太大时就要优化,这个优化我暂时看不太懂,但是时间复杂度是n*m*logs,我先把题目和代码贴出来,后期再遇到题目的时候在回过来看。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[25000];
int w[25000];
int f[25000];
int main()
{
cin>>n>>m;
int cnt=0;
for(int i=1;i<=n;i++)
{
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s)
{
cnt++;
v[cnt]=a*k;
w[cnt]=b*k;
s-=k;
k*=2;
}
if(s>0)
{
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
n=cnt;
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;
}
五:分组背包问题
先看分组背包问题的情形。
很明显从题目上看,这是以组为单位进行计算的,其实可以考虑成以组为单位的01背包问题,我们用最常用的思路建立模型。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[105][105];
int w[105][105];
int s[105];
int f[105][105];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s[i];
for(int j=0;j<s[i];j++)
cin>>v[i][j]>>w[i][j];
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];//不选第i个的时候
for(int k=0;k<=s[i];k++)//选第i个时候要选那一个要更新出来
if(v[i][k]<=j) f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
}
cout<<f[n][m];
return 0;
}
背包问题暂时结束!!!!