(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题

文章目录

    • 4. 动态规划(二)背包问题
      • 4.1 0-1背包
        • luogu1048采药
        • Acwing1022宠物小精灵之收服
      • 4.2 完全背包
        • acwing3完全背包问题
      • 4.3 分组背包
        • acwing9分组背包问题
      • 4.4 多重背包
        • acwing4多重背包问题Ⅰ
        • acwing5多重背包 Ⅱ

小标题的超链接为原题链接,点击跳转

4. 动态规划(二)背包问题

背包也属于简单基础DP,单独拎出来讲

根据维基百科,背包问题(Knapsack
problem)是一种组合优化的NP完全(NP-Complete,NPC)问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。NPC问题是没有多项式时间复杂度的解法的,但是利用动态规划,我们可以以伪多项式时间复杂度求解背包问题。
———————————————— 版权声明:本文为CSDN博主「yuyan_jia」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yuyan_jia/article/details/119817834

要点:

  • 分组背包,多重背包都是01背包的拓展
  • 所有背包问题都是先枚举物品种类i再枚举体积j,如果有k则枚举k
  • 完全背包问题的滚动数组形式,j是从前往后枚举,而01背包(包括分组和多重背包)的j则是从后往前枚举,原因取决于状态转移方程的方向

4.1 0-1背包

luogu1048采药

题目

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第1张图片

考点:

二维dp:一个花费一个价值的

本题也可以用滚动数组压缩状态至一维dp

代码

// 采药
#include 
using namespace std;

const int N = 110, T = 1010;

int dp[N][T];
int t, n;
int w[N], v[N];

int main()
{
	cin >> t >> n;

	for(int i=1; i<=n; i++) cin >> w[i] >> v[i];

	for(int i=1; i<=n; i++)
	{
		for(int j=0; j<=t; j++)
		{
		if(w[i] > j) dp[i][j] = dp[i-1][j];
		else dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
		}
	}
	cout << dp[n][t];

}

Acwing1022宠物小精灵之收服

题目

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第2张图片

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第3张图片

考点:

二维dp+滚动数组:题目中给出的是两个花费,一个价值,需要用滚动数组的思想将三维dp压缩状态至二维dp

代码

// 宠物小精灵之收服
#include 
using namespace std;

const int N = 1E3+10, M = 5E2 + 10, K = 1E2 + 10;
int dp[N][M];
int v1[N], v2[M];
int n, m, k;

int main()
{
	cin >> n >> m >> k;
	int topm = m--; // 难点:保留最大生命值,但是最大生命值不可用,因为皮卡丘死亡没有计算意义

	for(int i=1; i<=k; i++)
	{
		int v1, v2;
		cin >> v1 >> v2;
		for(int j=n; j>=0; j--)
			for(int k=m; k>=0; k--)
				if(v1 <= j && v2 <= k) dp[j][k] = max(dp[j][k], dp[j-v1][k-v2]+1);
	}

	cout << dp[n][m] << " ";
	int ans = m;
	while(ans > 0 && dp[n][ans-1] == dp[n][m]) ans--;
	cout << topm - ans;
}

4.2 完全背包

acwing3完全背包问题

题目

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第4张图片

代码

#include

using namespace std;

const int N = 1e3 + 10;
int dp[N][N];

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; j++)
        {
            if(vi <= j)
                dp[i][j] = max(dp[i-1][j], dp[i][j-vi] + wi);
            else dp[i][j] = dp[i-1][j];
        }
    }
    cout << dp[n][V];
}

4.3 分组背包

acwing9分组背包问题

题目

代码

// acwing9分组背包问题
#include 
using namespace std;

const int N = 1e2+10;
int S[N], v[N][N], w[N][N];
int dp[N];

int main()
{
	int n, m;
	cin >> n >> m;
	for(int i=1; i<=n; i++)
	{
		cin >> S[i];
		for(int j=1; j<=S[i]; j++)
		{
			cin >> v[i][j] >> w[i][j];
		}
	}
	for(int i=1; i<=n; i++)
		for(int j=m; j>=0; j--)
			for(int k=1; k<=S[i]; k++) // 这里j和k的枚举顺序不能替换
				if(j >= v[i][k]) dp[j] = max(dp[j], dp[j-v[i][k]] + w[i][k]);
	cout << dp[m];
}

4.4 多重背包

acwing4多重背包问题Ⅰ

题目

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第5张图片

代码

#include
using namespace std;

const int N = 1E2 + 10;
int dp[N][N];


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 j=0; j<=V; j++)
        {
            for(int k=0; k<=si; k++)// 将每种物品取0,1,2,..,k个共k+1个都当作一个种类
            {
                // 这里要注意k一定要囊括0,即k属于[0, si],因为k=0时实现了dp[i][j] = dp[i-1][j]
                // 此外这里max函数第一个和01背包不同的在于是dp[i][j]而非dp[i-1][j],因为物品种类没有改变,只改变了取的数量k,这是由dp数组定义所决定的
                if(vi*k <= j) dp[i][j] = max(dp[i][j], dp[i-1][j-vi*k] + wi*k);          
            }
        }

    }
    cout << dp[n][V];
}

acwing5多重背包 Ⅱ

题目

(实时更新)蓝桥杯知识点笔记 | (六)动态规划之背包问题_第6张图片

代码

// acwing5多重背包问题II
#include 
using namespace std;

const int N = 11*2e3+10; // 11是log(2e3)好像

int dp[N];
int v[N], w[N];
int n, V;
int cnt = 0;

int main()
{
	cin >> n >> V;
    
    // 二进制分类
	for(int i=1; i<=n; i++)
	{
		int vi, wi, si;
		cin >> vi >> wi >> si;
		int k = 1;
		while(k <= si)
		{
			cnt++;
			v[cnt] = vi * k;
			w[cnt] = wi * k;
			si -= k;
			k *= 2;
		}
		if(si)
		{
			cnt++;
			v[cnt] = vi * si;
			w[cnt] = wi * si;
		}
	}
   
    // 一维01背包模板
	for(int i=1; i<=cnt; i++)
	{
		for(int j=V; j>=v[i]; j--)
		{
			dp[j] = max(dp[j], dp[j-v[i]] + w[i]); 
		}
	}
	cout << dp[V];
}

你可能感兴趣的:(#,蓝桥杯,蓝桥杯,动态规划,算法)