• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
24
博客园    首页    新随笔    联系   管理    订阅  订阅

DP背包问题模板

【01背包】(十分重要)

采药 [P1048]

#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;
}

 

【完全背包】

货币系统 [P5020]

#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;
}

 

【多人背包】

多人背包 [P1858]

(求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;
 } 

 

【分组背包】

金明的预算方案 [P1064]

#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;
}

 

【多重背包】

AcWing 4 多重背包问题

#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;
}

 

宝物筛选 [P1776]

(二进制优化)

#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;
 } 

 

【二维费用】

找啊找啊找 GF [P1509]

#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;
}

 

【混合背包】

樱花 [P1833]

#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背包+并查集】

选学霸 [P2170]

#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背包模型。

完全背包则是无限次选取。

 

posted @ 2021-11-29 22:48  wxk1213  阅读(77)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3