背包问题

背包问题

01背包问题

  • 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次.
  • 第 i 件物品的体积是 vi,价值是 wi.
  • 求总体积不超过背包容量情况下的最大价值
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 10;
int dp[MAXN] = {0};
int main()
{
	int n,v;cin >> n >> v;
	for(int i = 1;i <= n;i++)
	{
		int vi,wi;cin >> vi >> wi;
		for(int j = v;j >= vi;j--)
		dp[j] = max(dp[j],dp[j - vi] + wi); 
	}
	cout << dp[v];
}
  • 从后往前遍历就可以实现一次物品的更新
  • 从前往回便是无限次物品的更新

完全背包问题

  • 每种物品都有无限件可用
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e3 + 10;
int dp[MAXN] = {0};
int main()
{
	int n,v;cin >> n >> v;
	for(int i = 1;i <= n;i++)
	{
		int vi,wi;cin >> vi >> wi;
		for(int j = 0;j <= v - vi;j++)
		dp[j + vi] = max(dp[j + vi],dp[j] + wi); 
	}
	cout << dp[v];
}
  • 从前往回遍历即可

多重背包问题

  • 限制物品的数量
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e2 + 10;
int dp[MAXN] = {0};
int main()
{
	int n,v;cin >> n >> v;
	for(int i = 1;i <= n;i++)
	{
		int vi,wi,si;cin >> vi >> wi >> si;
		for(int k = 1;k <= si;k++)
		for(int j = v;j >= vi;j--)
		dp[j] = max(dp[j],dp[j - vi] + wi);
	}
	cout << dp[v];
}
  • 从后往前为一次一次,那么只要来s次从后往前遍历即可(在数据量较小的情况下)

多重背包问题 II

  • 同上,当数据量较大时
  • 使用二进制优化
  • 如10010110b可以分解为01111111b + 00010111b,而如果将二进制的1表示一次计算,那么这个计算的值的范围便是[0~10010110b]
  • 从后往前遍历10010110b如果si&1 == 1就执行两次遍历,否者执行一次,然后si>>=1,直到si < 2
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 2e3 + 10;
int dp[MAXV] = {0};
int main()
{
	int n,v;cin >> n >> v;
	for(int i = 1;i <= n;i++)
	{
		int vi,wi,si;cin >> vi >> wi >> si;
		int ptop = 0;
		while(si >= 2)
		{
			if(si&1)
			for(int j = v;j >= vi*(1 << ptop);j--)
			dp[j] = max(dp[j],dp[j - vi * (1 << ptop)] + (1 << ptop) * wi);
			for(int j = v;j >= vi*(1 << ptop);j--)
			dp[j] = max(dp[j],dp[j - vi * (1 << ptop)] + (1 << ptop) * wi);
			ptop++;
			si >>= 1;
		}
		for(int j = v;j >= vi;j--)
		dp[j] = max(dp[j],dp[j - vi] + wi);
	}
	cout << dp[v];
}

多重背包问题 III

  • 单调队列优化
  • 滚动数组
#include<bits/stdc++.h>
using namespace std;
const int MAXV = 2e4 + 10;
int dp[2][MAXV] = {0};
int deq[MAXV];//单调队列
int main()
{
	ios::sync_with_stdio(false);//写了using namespace std;
	int n,v;cin >> n >> v;
	int t = 0;
	for(int i = 1;i <= n;i++)
	{
		int vi,wi,si;cin >> vi >> wi >> si;
		for(int j = 0;j < vi;j++)//枚举余数
		{
			int ptop = 0,pend = 0;//队头,队尾
			deq[0] = 0;
			for(int k = 1;v >= k * vi + j;k++)
			{
				while(pend >= ptop && k - deq[ptop] > si)
				ptop++;
				int tem = dp[1 - t][k * vi + j];
				if(pend >= ptop)
					dp[t][k * vi + j] = max(tem,dp[1 - t][deq[ptop] * vi + j] + wi * (k - deq[ptop]));
				while(pend >= ptop && tem >= dp[1 - t][deq[pend] * vi + j] + wi * (k - deq[pend]))
					pend--;
				deq[++pend] = k;
			}
		}
		t = 1 - t;
	}
	cout << dp[1 - t][v];
}

混合背包问题

  • 把无限次的看成非常大就可以

二维费用的背包问题

#include<bits/stdc++.h>
using namespace std;
const int MAXV = 101;
const int MAXM = 101;
int dp[MAXM][MAXV] = {0};
int main()
{
	int n,v,m;cin >> n >> v >> m;
	for(int i = 1;i <= n;i++)
	{
		int vi,mi,wi;cin >> vi >> mi >> wi;
		for(int j = v;j >= vi;j--)
		for(int k = m;k >= mi;k--)
		dp[k][j] = max(dp[k][j],dp[k - mi][j - vi] + wi);
	}
	cout << dp[m][v];
}

分组背包问题

  • 一组里只能拿一个
  • 首先只能拿一个,所以从后往前遍历
  • 一组内的物品不相互影响,所以用两个数组代表一组前和一组后的价值
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 101;
int dp[2][MAXN] = {0};
int main()
{
	int n,v,t = 1;cin >> n >> v;
	while(n--)
	{
		int si;cin >> si;
		for(int i = 1;i <= si;i++)
		{
			int vi,wi;cin >> vi >> wi;
			for(int j = v;j >= vi;j--)
			dp[t][j] = max(dp[t][j],dp[1 - t][j - vi] + wi);
		}
		for(int i = 1;i <= v;i++)
		dp[1 - t][i] = dp[t][i];
	}
	cout << dp[t][v];
}
posted @   xxcdsg  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示