第八周:背包问题

首先分享一下我学习背包问题时遇到的好文章dd大牛的背包九讲这个文章注重思路,入门必备

这一篇会有模板代码,且讲解细致
 

目录

第一道题:疯狂的采药

思路:

代码:

第二道题:樱花

思路:

代码:

第三道题:摆花

 思路:

代码:

第四道题:金明的预算方案

思路: 

代码:


第一道题:疯狂的采药

P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:

这是一道很简单的完全背包的模板题,只需要记住模板即可,如果想要知道相应的原理可以去看看上面分享的俩篇文章,但是要注意一点的是本题最多有 10^7的 时间,每种草药的价值最大是 10^4,所以极限情况下价值总和是 10^7*10^4=10^11,会爆 int,所以要开 long long。

//完全背包的板子	
for (int i = 1; i <= m; i++)//不同的物品
	{
		for (int j = t[i]; j <= T; j++)//时间从小到大
		{
			f[j] = max(f[j], f[j - t[i]] + v[i]);
		}
	}
//01背包的板子
for (int i = 1; i <= m; i++)//不同的物品
	{
		for (int j = T; j >=t[i]; j--)//时间从大到小
		{
			f[j] = max(f[j], f[j - t[i]] + v[i]);
		}
	}

代码:

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

第二道题:樱花

P1833 樱花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路:

 其实是一道混合背包的题,有俩种方法,一种是暴力法,对于大于1的花直接枚举出来看一次到看n次的情况看取不取,而还有一种方法则是二进制拆分,同时对于完全背包也有俩种做法,第一种是将其看作一个很大的数比如10000,再按照上面的方法写,还有一种则是进行分类,对其单独采用完全背包的处理方式。

代码:

多重背包简单的解法
#include<bits/stdc++.h>
using namespace std;
int N, V;
int f[2020];
int main()
{
 int h1, h2, m1, m2;
	char c;
	cin >> h1 >> c >> m1 >> h2 >> c >> m2;
	m = (h2 - h1) * 60 + m2 - m1;
	cin >> N ;
	for (int i = 1; i <= N; i++)
	{
		int v, w, s;
		cin >> v >> w >> s;
		for (int j = V;j >= v; j--)
		{
			for (int k = 1; k <= s && k * v <= j; k++)
			{
				f[j] = max(f[j], f[j - k * v] + k * w);
			}
		}
	}
	cout << f[V] << endl;
	return 0;
}
混合背包问题二进制
#include<bits/stdc++.h>
using namespace std;
int n, m;
int f[10005];
int main()
{
	vector<pair<int, int>>goods;//first为体积second为价值
	int h1, h2, m1, m2;
	char c;
	cin >> h1 >> c >> m1 >> h2 >> c >> m2;
	m = (h2 - h1) * 60 + m2 - m1;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int v, w, s;
		cin >> v >> w >> s;
		if (s == 0)s = 10000;
		for (int j = 1; j <= s; j *= 2)
		{
			s -= j;
			goods.push_back({ j * v,j * w });
		}
		if (s > 0)
		{
			goods.push_back({ s * v,s * w });
		}
	}
	for (int i = 0; i < goods.size(); i++)
	{
		for (int j = m; j >= goods[i].first; j--)
		{
			f[j] = max(f[j], f[j - goods[i].first] + goods[i].second);
		}
	}
	cout << f[m] << endl;
	return 0;
}
混合背包分类二进制
#include<bits/stdc++.h>
using namespace std;
int n, m;
int f[2020];
struct thing
{
	int kind;
	int v, m;
};
vector<thing>things;
int main()
{
	int h1, h2, m1, m2;
		char c;
		cin >> h1 >> c >> m1 >> h2 >> c >> m2;
		m = (h2 - h1) * 60 + m2 - m1;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		int v, w, s;
		cin >> v >> w >> s;
		if (s == 0)things.push_back({ 0,v,w });
		else
		{
			for (int j = 1; j <= s; j *= 2)
			{
				s -= j;
				things.push_back({1, v * j, w * j});
			}
			if (s > 0)
			{
				things.push_back({ 1,v * s,w * s });
			}
		}
	}
	for (auto thing : things)//遍历
	{
		if (thing.kind == 0)//完全背包
		{
			for (int i = thing.v; i <= m; i++)
			{
				f[i] = max(f[i], f[i - thing.v] + thing.m);
			}
		}
		else//01背包
		{
			for (int i = m; i >= thing.v; i--)
			{
				f[i] = max(f[i], f[i - thing.v] + thing.m);
			}
		}
	}
	cout << f[m] << endl;
	return 0;
}

第三道题:摆花

P1077 [NOIP2012 普及组] 摆花 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 思路:

这道题的代码其实并不难,只要理清如何摆花就可以了,首先,我们可以根据花的次序依次来选是摆1个还是到a(i)(a(i)表示第i种花最多摆多少次),且注意判断到后面应该是剩余的空间v和a(i)的较小值为第i个最多摆几次的上限,并且我们可以很容易写出二维的状态转移方程,f[i][j]表明在第i种花,且已经占用j个位置时的方案数

f[i][j]=f[i-1][j](表明当不放第i种的情况的方案数)+f[i-1][j-k](表明放第i种花的情况且放置的数量为k时的方案数)

根据二维的很容易得到答案为f[n][m],再用滚动数组优化一下就可以得到答案了,同时注意初始化

f[0][0]应该是等于1

代码:

#include<bits/stdc++.h>//滚动数组状态转移方程为f[j]=(f[j]+f[j-k])前面的f[j]为f[i][j]后面的f[j]为f[i-1][j]一开是不取第i个,后面是挨个取完第i个,俩者之和为f[i][j]
using namespace std;
int a[105], f[105];
int mod = 1000007;
int main()
{
	int n, m;
	cin >> n >> m;
	f[0] = 1;
	for (int i = 1; i <= n; i++)
	{
		int a;
		cin >> a;
		for (int j = m; j > 0; j--)
		{
			for (int k = 1; k <= min(a, j); k++)
			{
				f[j] = (f[j] + f[j - k]) % mod;
			}
		}
	}
	cout << f[m];
	return 0;
}

第四道题:金明的预算方案

P1064 [NOIP2006 提高组] 金明的预算方案 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

思路: 

这看似是个有依赖的背包问题,而实际上可以转化为多几种情况的01背包问题,主要是一共就5钟情况①不买主件 ②买主件 ③买主件+副件1 ④买主件+副件2 ⑤买主件+副件1+副件2,然后注意判断是否能够买下的边界条件判断就可以了

代码:

#include<iostream>  
using namespace std;
const int N = 32000;
int m, n, mw[N], mv[N], fw[N][3], fv[N][3], f[N], v, p, q;//mw主件重量,mv主件价值,fw主件对应的附件重量,fv主副价值,n总重量,m总个数   
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> v >> p >> q;
        if (!q) {//如果是主件   
            mw[i] = v;//主件重量   
            mv[i] = v * p;//主件价值与重量乘积   
        }
        else {//如果是附件   
            fw[q][0]++;//用来记录是第q个主件的第几个附件
            fw[q][fw[q][0]] = v;
            fv[q][fw[q][0]] = v * p;  
        }
    }
    for (int i = 1; i <= m; i++)
        for (int j = n; j >= mw[i]; j--) {//01背包模板   
            //每一个if的前提是背包能不能装下该物品   
                //情况1:只要主件 和啥都不要比较   
            f[j] = max(f[j], f[j - mw[i]] + mv[i]);
            //情况2:主件和附件1 和上面选出的较大值比较   
            if (j >= mw[i] + fw[i][1])f[j] = max(f[j], f[j - mw[i] - fw[i][1]] + mv[i] + fv[i][1]);
            //情况3:主件和附件2 和上面选出的较大值比较   
            if (j >= mw[i] + fw[i][2])f[j] = max(f[j], f[j - mw[i] - fw[i][2]] + mv[i] + fv[i][2]);
            //情况4:都要   
            if (j >= mw[i] + fw[i][1] + fw[i][2])
                f[j] = max(f[j], f[j - mw[i] - fw[i][1] - fw[i][2]] + mv[i] + fv[i][1] + fv[i][2]);
        }//因为开在全局区则初值为0,所以就算没有三个附件,有一个或者俩个是空的也不影响最后的结果
    //输出在价值为n时能得到的最大值   
    cout << f[n] << endl;
    return 0;

为了知识的全面性想了解二维背包问题分组背包问题可以自行去刷题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值