背包问题
AcWing 2. 01背包问题
01背包:前i个物品,背包容量j下的最优解
初始状态:dp[0][0]=0;
状态转移:
1.当前容量够,选择放入背包
2.当前不选择
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,volume[N],value[N],dp[N][N];
//dp[i][j]:j体积下前i个物品的最大价值
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i]>>value[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=volume[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-volume[i]]+value[i]);
}
}
cout<<dp[n][m]<<endl;
return 0;
}
二维--->一维,优化:
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,volume[N],value[N],dp[N];
//dp[j]:N件物品,背包容量j下的最优解
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i]>>value[i];
for(int i=1;i<=n;i++)
//逆序枚举+背包容量>=volume[i]时才会更新状态
for(int j=m;j>=volume[i];j--)
dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
cout<<dp[m]<<endl;
return 0;
}
补充练习:
P1048 [NOIP2005 普及组] 采药
#include<iostream>
using namespace std;
const int N=1e3+5;
int t,m,timee[N],value[N],dp[N][N];
int main(){
cin>>t>>m;
for(int i=1;i<=m;i++) cin>>timee[i]>>value[i];
for(int i=1;i<=m;i++){
for(int j=1;j<=t;j++){
dp[i][j]=dp[i-1][j];
if(j>=timee[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-timee[i]]+value[i]);
}
}
cout<<dp[m][t]<<endl;
return 0;
}
P1060 [NOIP2006 普及组] 开心的金明
#include<iostream>
using namespace std;
const int N=3e4+5;
int n,m,dp[30][N],money[30],important[30];
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>money[i]>>important[i];
important[i]*=money[i];
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
dp[i][j]=dp[i-1][j];
if(j>=money[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-money[i]]+important[i]);
}
}
cout<<dp[m][n]<<endl;
return 0;
}
P1734 最大约数和
注意:把题目转换成01背包问题以及约数和函数的编写:
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,dp[N][N],value[N],volume[N];
int sum(int x){
int res=0;
for(int i=1;i<x;i++) if(x%i==0) res+=i;
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
volume[i]=i;
value[i]=sum(i);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j]=dp[i-1][j];
if(j>=volume[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-volume[i]]+value[i]);
}
}
cout<<dp[n][n]<<endl;
return 0;
}
P1507 NASA的食物计划
#include<iostream>
using namespace std;
const int N=50;
int n,maxvolume,maxweight,dp[N][405][405],value[N],volume[N],weight[N];
int main(){
cin>>maxvolume>>maxweight>>n;
for(int i=1;i<=n;i++) cin>>volume[i]>>weight[i]>>value[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=maxvolume;j++){
for(int k=1;k<=maxweight;k++){
dp[i][j][k]=dp[i-1][j][k];
if(j>=volume[i]&&k>=weight[i])
dp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-volume[i]][k-weight[i]]+value[i]);
}
}
}
cout<<dp[n][maxvolume][maxweight]<<endl;
return 0;
}
P1164 小A点菜
#include<iostream>
using namespace std;
long long n,m,dp[105][10005],volume[105];
//dp[i][j]表示:只用前i种菜并且总钱数为j的所有方案数
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i];
for(int i=0;i<=n;i++) dp[i][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=volume[i]) dp[i][j]+=dp[i-1][j-volume[i]];
}
}
cout<<dp[n][m]<<endl;
return 0;
}
AcWing 3. 完全背包问题
与01背包区别:物品的个数可以在可选范围内任意选择
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,volume[N],value[N],dp[N][N];
//dp[i][j]:j体积下前i个物品的最大价值
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i]>>value[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k*volume[i]<=j;k++){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*volume[i]]+k*value[i]);
}
}
}
cout<<dp[n][m]<<endl;
return 0;
}
递推优化第三重循环:
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,volume[N],value[N],dp[N][N];
//dp[i][j]:j体积下前i个物品的最大价值
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i]>>value[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=volume[i]) dp[i][j]=max(dp[i][j],dp[i][j-volume[i]]+value[i]);
}
}
cout<<dp[n][m]<<endl;
return 0;
}
优化:二维-->一维:
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,volume[N],value[N],dp[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i]>>value[i];
for(int i=1;i<=n;i++)
for(int j=volume[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-volume[i]]+value[i]);
cout<<dp[m]<<endl;
return 0;
}
AcWing 1371. 货币系统
#include<iostream>
using namespace std;
long long n,m,dp[30][10005],volume[30];
//dp[i][j]表示:只用前i种货币并且总钱数为j的所有方案数
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>volume[i];
for(int i=0;i<=n;i++) dp[i][0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=dp[i-1][j];
if(j>=volume[i]) dp[i][j]+=dp[i][j-volume[i]];
}
}
cout<<dp[n][m]<<endl;
return 0;
}
P8742 [蓝桥杯 2021 省 AB] 砝码称重
#include<iostream>
using namespace std;
int n,volume[105],sum,ans;
bool dp[105][200005];
//dp[i][j]:用前i个砝码是否可以组成j,注意这里的j要开两倍(有绝对值)
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>volume[i],sum+=volume[i];
dp[0][0]=true;
for(int i=1;i<=n;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=dp[i-1][j]||dp[i-1][j+volume[i]]||dp[i-1][abs(j-volume[i])];
}
}
for(int i=1;i<=sum;i++) if(dp[n][i]) ans++;
cout<<ans<<endl;
return 0;
}
AcWing 4. 多重背包问题 I
#include<iostream>
using namespace std;
const int N=105;
int n,m,v[N],w[N],s[N],dp[N][N];
int main(){
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<=s[i]&&k*v[i]<=j;k++){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
}
}
}
cout<<dp[n][m]<<endl;
return 0;
}
AcWing 5. 多重背包问题 II
#include<iostream>
#include<vector>
using namespace std;
int n,m,dp[2005];
struct good{
int v,w;
};
vector<good>goods;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int v,w,s;
cin>>v>>w>>s;
for(int k=1;k<=s;k*=2){
s-=k;
goods.push_back({v*k,w*k});
}
if(s>0) goods.push_back({v*s,w*s});
}
for(auto x:goods)
for(int j=m;j>=x.v;j--)
dp[j]=max(dp[j],dp[j-x.v]+x.w);
cout<<dp[m]<<endl;
return 0;
}
AcWing 9. 分组背包问题
#include<iostream>
using namespace std;
const int N=105;
int n,m,v[N],w[N],dp[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int s;
cin>>s;
for(int x=1;x<=s;x++) cin>>v[x]>>w[x];
for(int j=m;j>=0;j--)
for(int k=1;k<=s;k++)
if(j>=v[k]) dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
}
cout<<dp[m]<<endl;
return 0;
}
线性DP
AcWing 898. 数字三角形
#include<iostream>
using namespace std;
const int N=505;
int n,dp[N][N],a[N][N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>a[i][j];
for(int i=1;i<=n;i++) dp[n][i]=a[n][i];
for(int i=n-1;i>=1;i--)
for(int j=1;j<=i;j++)
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
cout<<dp[1][1]<<endl;
return 0;
}
AcWing 895. 最长上升子序列
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+5;
int n,a[N],dp[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],dp[i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i-1;j++)
if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1);
sort(dp+1,dp+1+n);
cout<<dp[n]<<endl;
return 0;
}
AcWing 896. 最长上升子序列 II
贪心+二分(优化):
#include<iostream>
using namespace std;
const int N=1e5+5;
int n,len,a[N],b[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
b[0]=-2e9,len=0;
for(int i=1;i<=n;i++){
if(b[len]<a[i]) b[++len]=a[i];
else {
int l=1,r=len;
while(l<r){
int mid=(l+r)/2;
if(b[mid]>=a[i]) r=mid;
else l=mid+1;
}
b[l]=a[i];
}
}
cout<<len<<endl;
return 0;
}
AcWing 897. 最长公共子序列
#include<iostream>
using namespace std;
const int N=1e3+5;
int n,m,dp[N][N];
char a[N],b[N];
int main(){
cin>>n>>m>>a+1>>b+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
}
}
cout<<dp[n][m]<<endl;
return 0;
}