【若归】背包dp做题笔记

前言:

现在决定未来,未来与过去无关。--波波

前置知识:

dd_engi的背包九讲(新版转载)
|
背包九讲——全篇详细理解与代码实现

背包问题 (附单调队列优化多重背包
|
背包问题入门(单调队列优化多重背包

多重背包问题入门+dp储存空间优化

动态规划——各类背包及优化问题详解

题目来自学校OJ、洛谷、ybtOJ。

AtCoder dp 26 题

背包精选

背包问题(简单)

背包问题

学校OJ:

A.采药

P1048 [NOIP2005 普及组] 采药

01背包(后面解题过程大部分均由01背包变形)板子题,加上一个滚动数组优化,没什么好说的。

$ 1<=i<=m,t>=j>=w[i]$

$f[j]=max(f[j],f[j-w[i]]+c[i]) $

#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],f[10001];
int main()
{
	int t,m,i,j;
    cin>>t>>m;
    for(i=1;i<=m;i++)
    {
        cin>>w[i]>>c[i];
	}
	for(i=1;i<=m;i++)
    {
        for(j=t;j>=w[i];j--)
   		{
    	    f[j]=max(f[j],f[j-w[i]]+c[i]);
		}
    }
	cout<<f[t]<<endl;
    return 0;
}

B.开心的金明

P1060[NOIP2006 普及组] 开心的金明

与A类似,还是01背包板子,设第\(j\)件物品的价格为\(v[j]\),重要度为\(w[j]\),则第\(j\)件物品的价值为\(v[j]*w[j](j<=n)\)

$ 1<=i<=m,n>=j>=v[i]$

$f[j]=max(f[j],f[j-v[i]]+p[i]) $

#include<bits/stdc++.h>
using namespace std;
int v[750001],p[750001],f[750001];
int main()
{
	int n,m,i,j;
    cin>>n>>m;
    for(i=1;i<=m;i++)
    {
        cin>>v[i]>>p[i];
        p[i]*=v[i];
	}
	for(i=1;i<=m;i++)
    {
        for(j=n;j>=v[i];j--)
   		{
    	    f[j]=max(f[j],f[j-v[i]]+p[i]);
		}
    }
	cout<<f[n]<<endl;
    return 0;
}

C. 完全背包问题

ybtOJ 1268:【例9.12】完全背包问题

完全背包板子(优化后面讲),因范围较小,选用不加优化的做法。

完全背包板子和和01背包板子仅有j的循环次序不同(注意一下,不要混淆),同时注意一下输出格式。

$ 1<=i<=n,w[i]<=j<=m$

$f[j]=max(f[j-w[i]]+c[i],f[j]) $

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,c[31],w[31],f[2001];
int main()
{
	cin>>m>>n;
	for(i=1;i<=n;i++)
	{
		cin>>w[i]>>c[i];
	}
	for(i=1;i<=n;i++)
	{
		for(j=w[i];j<=m;j++)
		{
			f[j]=max(f[j-w[i]]+c[i],f[j]);
		}
	}
	cout<<"max="<<f[m];
	return 0;
}

D.竞赛总分

P2722 [USACO3.1]总分 Score Inflation

完全背包板子(同C),加强一下印象。

$ 1<=i<=n,w[i]<=j<=m$

$f[j]=max(f[j-w[i]]+c[i],f[j]) $

#include<bits/stdc++.h>
using namespace std;
int c[10001],w[10001],f[10001];
int main()
{
	int n,m,i,j;
	cin>>m>>n;
	for(i=1;i<=n;i++)
	{
		cin>>c[i]>>w[i];
	}
	for(i=1;i<=n;i++)
	{
		for(j=w[i];j<=m;j++)
		{
			f[j]=max(f[j-w[i]]+c[i],f[j]);
		}
	}
	cout<<f[m];
	return 0;
}

E.砝码称重

P2347 [NOIP1996 提高组] 砝码称重

考虑两种解法,一种是暴力(时间复杂度为各砝码数量相乘),一种是背包dp(最坏情况下为\(6*1000^2\));记得初始化,注意输出格式。

暴力:

建议不要采用这种方式,或者加上特判(未写);如果是极限数据,就会被卡

#11
样例输入:65 34 34 23 23 21
样例输出:1000
#include<bits/stdc++.h>
using namespace std;
int c[1001];
int main()
{
	int a[7],b[7],i,sum,ans=0;
	cin>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]>>a[6];
	for(b[1]=0;b[1]<=a[1];b[1]++)
	{
		for(b[2]=0;b[2]<=a[2];b[2]++)
		{
			for(b[3]=0;b[3]<=a[3];b[3]++)
			{
				for(b[4]=0;b[4]<=a[4];b[4]++)
				{
					for(b[5]=0;b[5]<=a[5];b[5]++)
					{
						for(b[6]=0;b[6]<=a[6];b[6]++)
						{
							sum=b[1]+b[2]*2+b[3]*3+b[4]*5+b[5]*10+b[6]*20;
							if(c[sum]==0)
							{
								c[sum]=1;
								ans++;
							}
						}
					}
				}
			}
		}
	}
	ans--;
	cout<<"Total="<<ans;
	return 0;
}

背包dp:

思路是枚举每个砝码数量,若\(f[k]\)存在,则\(f[k+b[i]]\)一定存在;如果数据范围较大,可将f数组的类型从int改为bool。

#include<bits/stdc++.h>
using namespace std;
int a[7],b[7]={0,1,2,3,5,10,20},f[1001];
int main()
{
	int i,j,k,ans=0;
	for(i=1;i<=6;i++)
	{
		cin>>a[i];
	}
	f[0]=1;
	for(i=1;i<=6;i++)
	{
		for(j=1;j<=a[i];j++)
		{
			for(k=1000;k>=0;k--)
			{
			    if(f[k]==1)
				{
					if(f[k+b[i]]==0)
					{
						f[k+b[i]]=1;
						ans++;
					}
					
				}
			}
		}
	}
	cout<<"Total="<<ans;
	return 0;
 }

F.最小乘车费用

完全背包变形,枚举每种情况下的距离以达到状态转移。

$ 1<=i<=10,i<=j<=n$

$ f[j]=min(f[j-i]+w[i],f[j])$

#include<bits/stdc++.h>
using namespace std;
int f[10001],w[11];
int main()
{
	int n,i,j;
	memset(f,0x3f3f3f3f,sizeof(f));
	f[0]=0;
	for(i=1;i<=10;i++)
	{
		cin>>w[i];
	}
	cin>>n;
	for(i=1;i<=10;i++)
	{
		for(j=i;j<=n;j++)
		{
			f[j]=min(f[j-i]+w[i],f[j]);//用f[j-i]+w[i]和f[j]取最小值更新f[j].f[j-i]表示到j-i的最短花费,加上w[i]即可(走i公里需要的花费)。
		}
	}
	cout<<f[n];
	return 0;
}

G.逃亡的准备

初看到这道题以为是混合背包板子,然后就TLE了。(小声bb:我还以为是测评姬波动,前前后后交了170+次)

后来考2022CSP前,将混合背包的01背包和多重背包进行了二进制优化,于是AC。(如果数据再ex亿点,可能要用单调队列优化)

\(1<=i<=n\)

\(if(p[i]==0)\) $ v[i]<=j<=m$ \(f[j]=max(f[j],f[j-v[i]]+w[i])\)

\(else\) 详见代码

#include <bits/stdc++.h>
using namespace std;
int f[10001],v[10001],w[10001],p[10001];
int main()
{
	int n,m,i,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i]>>p[i];
        if(p[i]==0)//完全背包
        {
            for(j=v[i];j<=m;j++)
            {
			    f[j]=max(f[j],f[j-v[i]]+w[i]);
        	}
        }
        else//01背包可以当作多重背包来处理
        {
			for(k=1;k<=p[i];k*=2)
            {
                for(j=m;j>=k*v[i];j--)
                {
				    f[j]=max(f[j],f[j-k*v[i]]+k*w[i]);
            	}
				p[i]-=k;
            }
            if(p[i]>0)
            {
                for(j=m;j>=p[i]*v[i];j--)
                {
                    f[j]=max(f[j],f[j-p[i]*v[i]]+p[i]*w[i]);
                }
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

H.庆功会

ybtOJ 1269:【例9.13】庆功会

不加优化的多重背包即可,优化详见G。

\(1<=i<=n,m>=j>=0,0<=k<=num[i]\)

$ if(j-k*w[i]>=0)$ \(f[j]=max(f[j],f[j-k*w[i]]+k*c[i])\)

#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],num[10001],f[10001];
int main()
{
	int m,n,i,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>w[i]>>c[i]>>num[i];
    }
	for(i=1;i<=n;i++)
    {
	    for(j=m;j>=0;j--)
	    {
	        for(k=0;k<=num[i];k++)
	        {
	            if(j-k*w[i]>=0)//很重要,避免RE
	            {
	                f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
				}
			}
		}
	}
    cout<<f[m]<<endl;
    return 0;
}

I.混合背包

ybtOJ 1270:【例9.14】混合背包

不加优化的混合背包即可,优化详见G。

\(1<=i<=n\)

\(if(num[i]==1)\) $ m>=j>=w[j]$ \(f[j]=max(f[j],f[j-w[i]]+c[i])\)

\(if(num[i]==0)\) $ w[i]<=j<=m$ \(f[j]=max(f[j],f[j-w[i]]+c[i])\)

\(if(num[i]>1)\) \(m>=j>=0,0<=k<=num[i]\) \(if(j-k*w[i]>=0)\) \(f[j]=max(f[j],f[j-k*w[i]]+k*c[i])\)

#include<bits/stdc++.h>
using namespace std;
int w[10001],c[10001],num[10001],f[10001];
int main()
{
	int m,n,i,j,k;
    cin>>m>>n;
    for(i=1;i<=n;i++)
    {
        cin>>w[i]>>c[i]>>num[i];
	}
    for(i=1;i<=n;i++)
    {
        if(num[i]==1)
        {
		    for(j=m;j>=w[i];j--)
		    {
		        f[j]=max(f[j],f[j-w[i]]+c[i]);
			}
        }
		else 
		{
			if(num[i]==0)
            {
		        for(j=w[i];j<=m;j++)
		    	{
		       		f[j]=max(f[j],f[j-w[i]]+c[i]);
				}
        	}
			else
            {
			    for(j=m;j>=0;j--)
			    {
			        for(k=0;k<=num[i];k++)
			        {
			            if(j-k*w[i]>=0)
			            {
			                f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
						}
					}
				}
 		   	}
		}
 	}
    cout<<f[m]<<endl;
    return 0;
}

J.NASA的食物计划

P1507 NASA的食物计划

二维费用的背包板子。(教练说非常简单)

\(1<=i<=q,p>=j>=w[i],q>=k>=v[i]\) \(f[j][k]=max(f[j][k],f[j-w[i]][k-v[i]]+c[i])\)

#include<bits/stdc++.h>
using namespace std;
int w[401],v[401],c[401],f[401][401];
int main()
{
	int p,q,n,i,j,k;
	cin>>p>>q>>n;
	for(i=1;i<=n;i++)
	{
		cin>>w[i]>>v[i]>>c[i];
	}
	for(i=1;i<=q;i++)
	{
		for(j=p;j>=w[i];j--)
		{
			for(k=q;k>=v[i];k--)
			{
				f[j][k]=max(f[j][k],f[j-w[i]][k-v[i]]+c[i]);
			}
		}
	}
	cout<<f[p][q];
	return 0;
}

K. 分组背包

ybtOJ 1272:【例9.16】分组背包

分组背包板子。

每组物品有若干种策略:选本组的某一件,或者一件也不选。即设 \(f[k][j]\) 表示前 \(k\) 组物品花费费用 \(j\) 能取得的最大权值,则有$ f[k][j]=max(f[k-1][j],f[k-1][j-w[i]+c[i]|物品i属于第k组)$。接着将其用滚动数组优化即可(转换为一维数组)。

\(1<=k<=t,v>=j>=0,1<=i<=group[k][0]\)

\(if(j>=w[group[i][k]]) f[j]=max(f[j],f[j-w[group[i][k]]]+c[group[i][k]])\)

#include<bits/stdc++.h>
using namespace std;
int group[1001][1001],w[1001],c[1001],f[1001];
int main()
{
    int v,n,t,i,j,k,p,q;
    cin>>v>>n>>t;
    for(i=1;i<=n;i++)
    {
        cin>>w[i]>>c[i]>>p;
		group[p][0]++;
        group[p][group[p][0]]=i;
    }
    for(i=1;i<=t;i++)
    {
	    for(j=v;j>=0;j--)
	    {
	        for(k=1;k<=group[i][0];k++)
	        {
	            q=group[i][k];
	            if(j>=w[q])
	            {
	                f[j]=max(f[j],f[j-w[q]]+c[q]);
	        	}
	        }
	    }
    }
	cout<<f[v]<<endl;
    return 0;
}

L.新年趣事之打牌

Vijos1071 新年趣事之打牌

01背包的变形,判断是否有解及多组解,同时进行记录,方便后面输出。

#include<bits/stdc++.h>
using namespace std;
int w[100001],s[100001],f[100001],ans[100001];
int main()
{
	int n,num,i,j,sum;
	cin>>num>>n;
	for(i=1;i<=n;i++)
	{
		cin>>w[i];
	}
	s[0]=1;//初始化
	for(i=1;i<=n;i++)//01背包
    {
        for(j=num;j>=w[i];j--)
   		{
			if(s[j-w[i]]!=0)
			{
    	    	s[j]+=s[j-w[i]];
				if(f[j]==0)
				{
					f[j]=i;//初始化+记录路径
				}
			}
		}
    }
	if(s[num]==0)//无解
 	{
		cout<<"0";
	}
	if(s[num]==1)//有解
	{
		sum=num;
		while(sum>0)//记录路径(纸牌)
		{
			ans[f[sum]]=1;
			sum-=w[f[sum]];
		}
		for(i=1;i<=n;++i)//输出
		{
			if(ans[i]==0)
			{
				cout<<i<<" ";
			}
		}
	}
	if(s[num]>1)//有多组解
    {
    	cout<<"-1";
	}
	return 0;
}

M. 积木城堡

P1504 积木城堡

01背包变形,判断是否有解。

#include<bits/stdc++.h>
using namespace std;
int h[10001],num[10001],f[10001];
int main()
{
	int n,ans=0x3f3f3f3f,sum,i,j,k,p,flag=0;//初始化
	cin>>n;
	for(i=1;i<=n;i++)
	{
		memset(f,0,sizeof(f));//每次循环都要清空
		memset(h,0,sizeof(h));//每次循环都要清空(这条语句可要可不要,但还是带上的好)
		f[0]=1;//初始化
		p=1;
		sum=0;
		while(cin>>h[p])
		{
			if(h[p]==-1)
			{
				break;
			}
			else
			{
				sum+=h[p];
			}
			p++;
		}
		p--;//因为读入结束后,p会比实际长度多1,故p--
		ans=min(ans,sum);//ans为查询的最大值
		for(j=1;j<=p;j++)//01背包
		{
			for(k=sum;k>=h[j];k--)
			{
				if(f[k-h[j]]==1)
				{
					f[k]=1;
				}
			}
		}
		for(j=sum;j>=1;j--)//标记下
		{
			if(f[j]==1)
			{
				num[j]++;
			}
		}
	}
	for(i=ans;i>=1;i--)
	{
		if(num[i]==n)//有解
		{
			cout<<i;
			flag=1;
			break;
		}
	}
	if(flag==0)//无解
	{
		cout<<"0";
	}
	return 0;
}

洛谷:

AT_dp_e Knapsack 2

注意数据范围有亿点大,不能用正常的01背包解决。换一种方式,让f数组用来储存容量,即\(f[i]\)用来储存价值为 \(i\) 时所需的最小容量。

$ 1<=i<=t,1000*t>=j>=c[i]$

\(f[j]=max(f[j],f[j-c[i]]+w[i])\)

#include<bits/stdc++.h>
using namespace std;
int w[1000001],c[1000001],f[1000001];
int main()
{
	int t,m,i,j;
    cin>>t>>m;
    memset(f,0x3f,sizeof(f));
    for(i=1;i<=t;i++)
    {
        cin>>w[i]>>c[i];
	}
	f[0]=0;
	for(i=1;i<=t;i++)
    {
        for(j=1000*t;j>=c[i];j--)
   		{
    	    f[j]=min(f[j],f[j-c[i]]+w[i]);
		}
    }
    for(i=1000*t;i>0;i--)
    {
    	if(f[i]<=m)
    	{
    		cout<<i;
    		break;
		}
	}
    return 0;
}

P1776 宝物筛选

单调队列优化多重背包板子

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define endl '\n'
int f[100001],g[100001],w[100001],v[100001],p[100001];
int main()
{
    int n,m,i,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i]>>p[i];
        if(p[i]==0)
        {
            for(j=w[i];j<=m;j++)
            {
			    f[j]=max(f[j],f[j-w[i]]+v[i]);
        	}
        }
        else
        {
            memcpy(g,f,sizeof(f));
            for(j=0;j<w[i];j++)
            {
                deque<int>q;
                for(k=0;k<=(m-j)/w[i];k++)
                {
                    if(q.empty()==0&&q.front()<k-p[i])
                    {
                        q.pop_front();
                    }
                    while(q.empty()==0&&g[j+q.back()*w[i]]-q.back()*v[i]<=g[j+k*w[i]]-k*v[i])
                    {
                        q.pop_back();
                    }
                    q.push_back(k);
                    f[j+k*w[i]]=g[j+q.front()*w[i]]+(k-q.front())*v[i];
                }
            }
		}
    }
    cout<<f[m]<<endl;
    return 0;
}
/*
#include <bits/stdc++.h>
using namespace std;
int f[100001],w[100001],v[100001],p[100001];
int main()
{
	int n,m,i,j,k;
    cin>>n>>m;
    for(i=1;i<=n;i++)
    {
        cin>>v[i]>>w[i]>>p[i];
        if(p[i]==0)
        {
            for(j=w[i];j<=m;j++)
            {
			    f[j]=max(f[j],f[j-w[i]]+v[i]);
        	}
        }
        else
        {
			for(k=1;k<=p[i];k*=2)
            {
                for(j=m;j>=k*w[i];j--)
                {
				    f[j]=max(f[j],f[j-k*w[i]]+k*v[i]);
            	}
				p[i]-=k;
            }
            if(p[i]>0)
            {
                for(j=m;j>=p[i]*w[i];j--)
                {
                    f[j]=max(f[j],f[j-p[i]*w[i]]+p[i]*v[i]);
                }
            }
        }
    }
    cout<<f[m]<<endl;
    return 0;
}
*/
posted @ 2023-06-09 21:41  hzoi_Shadow  阅读(18)  评论(0编辑  收藏  举报
扩大
缩小