DP背包问题模板
【01背包】(十分重要)
#include<bits/stdc++.h>
using namespace std;
int n,t,dp[1005],a[105],b[105];
int main()
{
cin>>t>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
for(int i=1;i<=n;i++)
{
for(int j=t;j>=a[i];j--) //倒着循环是因为需要i-1的状态来更新
{ //如果正着就会改变i-1的状态
dp[j]=max(dp[j-a[i]]+b[i],dp[j]);
}
}
cout<<dp[t];
return 0;
}
【完全背包】
#include<bits/stdc++.h>
using namespace std;
int a[105],dp[25005];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,ans=0;
cin>>n;
memset(dp,-0x3f,sizeof dp);
for(int i=1;i<=n;i++)
cin>>a[i];
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i];j<=25000;j++)
{
dp[j]=max(dp[j-a[i]]+1,dp[j]);
}
}
for(int i=1;i<=n;i++)
ans+=dp[a[i]]==1;
cout<<ans<<endl;
}
return 0;
}
【多人背包】
(求01背包前k优解的价值和)
#include<bits/stdc++.h>
using namespace std;
int m,r,n,ans,re[55],v[210],w[210],dp[5010][55];
int main()
{
cin>>r>>m>>n;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
memset(dp,-0x3f,sizeof dp);
dp[0][1]=0;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
int a=1,b=1,t=0;
while(t<=r)
{
if(dp[j][a]>=dp[j-v[i]][b]+w[i]) re[++t]=dp[j][a++];
else re[++t]=dp[j-v[i]][b++]+w[i];
}
for(int k=1;k<=r;k++)
dp[j][k]=re[k];
}
}
for(int i=1;i<=r;i++)
ans+=dp[m][i];
cout<<ans;
return 0;
}
【分组背包】
#include<bits/stdc++.h>
using namespace std;
int m,n,mw[33333],mv[33333],fw[33333][3],fv[33333][3],dp[33333];
int main()
{
cin>>m>>n;
for(int i=1;i<=n;i++)
{
int x,y,z;
cin>>x>>y>>z;
if(!z)
{
mw[i]=x;
mv[i]=x*y;
}
else
{
fw[z][0]++;
fw[z][fw[z][0]]=x;
fv[z][fw[z][0]]=x*y;
}
}
for(int i=1;i<=n;i++)
{
for(int j=m;j>=mw[i];j--)
{
dp[j]=max(dp[j-mw[i]]+mv[i],dp[j]);
if(j>=mw[i]+fw[i][1])
dp[j]=max(dp[j-mw[i]-fw[i][1]]+fv[i][1]+mv[i],dp[j]);
if(j>=mw[i]+fw[i][2])
dp[j]=max(dp[j-mw[i]-fw[i][2]]+fv[i][2]+mv[i],dp[j]);
if(j>=mw[i]+fw[i][1]+fw[i][2])
dp[j]=max(dp[j-mw[i]-fw[i][1]-fw[i][2]]+fv[i][1]+fv[i][2]+mv[i],dp[j]);
}
}
cout<<dp[m];
return 0;
}
【多重背包】
#include<bits/stdc++.h>
using namespace std;
int n,v;
int a[105],b[105],c[105],dp[10005];
int main()
{
cin>>n>>v;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<=n;i++)
{
for(int j=v;j>=a[i];j--)
{
for(int k=0;k<=c[i]&&k*a[i]<=j;k++)
{
dp[j]=max(dp[j],dp[j-k*a[i]]+k*b[i]);
}
}
}
cout<<dp[v];
return 0;
}
(二进制优化)
#include<bits/stdc++.h>
using namespace std;
int n,m,t,dp[40005],v[100005],w[100005];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
int q=1; //转化为二进制数之和的形式分成几个物品
while(z>=q)
{
v[++t]=q*y;
w[t]=q*x;
z-=q;
q<<=1;
}
if(z)
v[++t]=z*y,w[t]=z*x;
}
for(int i=1;i<=t;i++) //01背包
{
for(int j=m;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
printf("%d",dp[m]);
return 0;
}
【二维费用】
#include<bits/stdc++.h>
using namespace std;
int n,m,r,tmp,dpn[105][105],dpt[105][105],v1[105],v2[105],w[105];
int main() //dpn是表示满足条件可以有多少个girls
{ //dpt则是表示最少的时间
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>v1[i]>>v2[i]>>w[i];
}
cin>>m>>r;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v1[i];j--)
{
for(int k=r;k>=v2[i];k--)
{
if(dpn[j][k]<(tmp=dpn[j-v1[i]][k-v2[i]]+1))//找最大人数
{
dpn[j][k]=tmp;
dpt[j][k]=dpt[j-v1[i]][k-v2[i]]+w[i];
}
else if(dpn[j][k]==tmp)//相等找最短时间
{
dpt[j][k]=min(dpt[j][k],dpt[j-v1[i]][k-v2[i]]+w[i]);
}
}
}
}
cout<<dpt[m][r];
return 0;
}
【混合背包】
#include<bits/stdc++.h>
using namespace std;
int a,b,c,d,n,q,dp[10005],v[100005],w[100005];
int main()
{
scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&n);
int t=c*60+d-a*60-b;
for(int i=1;i<=n;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(!z)
{
for(int i=x;i<=t;i++) //完全背包更新无数次的部分
dp[i]=max(dp[i],dp[i-x]+y);
}
else
{
int p=1;//任意一个数可以转化成二进制数将物品拆分成几个部分
while(z>=p)
{
v[++q]=p*x;
w[q]=p*y;
z-=p;
p<<=1;
}
v[++q]=z*x;
w[q]=z*y;
}
}
for(int i=1;i<=q;i++) //最后 01 背包板子
{
for(int j=t;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
printf("%d",dp[t]);
return 0;
}
【01背包+并查集】
#include<bits/stdc++.h>
using namespace std;
int n,m,k,maxx=1e8,ans,cnt,root[20005],v[20005],vv[20005],dp[200005];
int getroot(int x)
{
if(root[x]==x)
return x;
return getroot(root[x]);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
root[i]=i,v[i]=1;
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int tx=getroot(x);
int ty=getroot(y);
if(tx!=ty)
root[tx]=ty,v[ty]+=v[tx];
}
for(int i=1;i<=n;i++)
{
if(root[i]==i)
vv[++cnt]=v[i];
}
for(int i=1;i<=cnt;i++)
{
for(int j=2*m;j>=vv[i];j--)
{
dp[j]=max(dp[j],dp[j-vv[i]]+vv[i]);
}
}
for(int i=1;i<=2*m;i++)
{
if(abs(dp[i]-m)<maxx)
maxx=abs(dp[i]-m),ans=dp[i];
}
cout<<ans;
return 0;
}
注意:
大部分有限次的选取,都是01背包的变形,需要转化到01背包模型。
完全背包则是无限次选取。
浙公网安备 33010602011771号