首页 > 其他分享 >背包问题(多重背包与分组背包)

背包问题(多重背包与分组背包)

时间:2024-06-09 21:33:27浏览次数:18  
标签:多重 le 10 int leq 背包 分组 宝物

多重背包问题

与01背包的区别在于每个物品的个数有限制,且不一样。

f[i, j] = max(f[i-1,j-v[i]*k] + w[i]*k, k为选择放进背包里的当前物品的个数)

优化过程

对比两个状态转移方程

//其中s代表对于第i个物品而言限制的最大数量
f[i,j] = max(f[i-1, j], f[i-1, j-v]+w, f[i-2, j-2v]+ 2w,...,f[i-1, j-sv]+sw)
f[i, j-v] = max(f[i-1, j-v], f[i-1, j-2v]+w, f[i-1, j-3v]+2w,...,f[i-1,j-sv]+(s-1)w, f[i-1,j-(s+1)v]+sw)

发现\(f[i, j-v]\)相比于\(f[i, j]\)\(状态转移方程而言,多了一项\)\(f[i-1, j-(s+1)v+sw]\)

因此无法利用完全背包的思维进行优化。

在多重背包中,常用的优化方法为二进制优化

例:假设当前物品最多只能选1023件,可以将其拆分成:

数量为1, 2, 4, 8, ... 512的物品,每件物品最多只能选一次。

这样就能表示出来选取数量的所有方案

当s = 200时,应拆分为1, 2, 4, 8, 16,32, 64, 73(注意73)

代码做法:

先对所有物品,按照限制的数量\(s[i]\)进行拆分,可按照二进制优化的方法拆分成\(logs[i]\)个物品

再对所有物品拆分完之后的物品进行01背包的做法即可。

这样可以把枚举数量的第三层循环从\(O(s)\)缩小到\(O(logs)\)

分组背包问题

所有物品被分为几组,每一组的数量有限制,问最大的价值为多少。

状态转移方程

//k为在该组内限制数量的范围枚举
f[i, j] = max(f[i-1, j-v[i,k]+w[i,k]])

习题

1. 通天之分组背包

题目背景

直达通天路·小 A 历险记第二篇

题目描述

自 \(01\) 背包问世之后,小 A 对此深感兴趣。一天,小 A 去远游,却发现他的背包不同于 \(01\) 背包,他的物品大致可分为 \(k\) 组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。

输入格式

两个数 \(m,n\),表示一共有 \(n\) 件物品,总重量为 \(m\)。

接下来 \(n\) 行,每行 \(3\) 个数 \(a_i,b_i,c_i\),表示物品的重量,利用价值,所属组数。

输出格式

一个数,最大的利用价值。

样例 #1

样例输入 #1

45 3
10 10 1
10 5 1
50 400 2

样例输出 #1

10

提示

\(0 \leq m \leq 1000\),\(1 \leq n \leq 1000\),\(1\leq k\leq 100\),\(a_i, b_i, c_i\) 在 int 范围内。

参考代码

//P1757
//分组背包
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int f[105][1005];

struct wp{
    int a, b, c, d;
}w[1005];

bool cmp(wp x, wp y){
    return x.c < y.c;
}

int main(){
    int m, n;
    cin >> m >> n;
    for(int i = 1; i <= n; i++)     cin >> w[i].a >> w[i].b >> w[i].c;
    sort(w+1, w+n+1, cmp);
    int num = 1;
    w[1].d = num;
    for(int i = 2; i <= n; i++){
        if(w[i].c != w[i-1].c)  num ++;
        w[i].d = num;
    }
    for(int i = 1; i <= n; i++){
        int idx = w[i].d;
        for(int j = 0; j <= m; j++){
            f[idx][j] = max(f[idx][j], f[idx-1][j]);
            if(j >= w[i].a)       f[idx][j] = max(f[idx][j], f[idx-1][j-w[i].a] + w[i].b);
        }
    }
    cout << f[w[n].c][m] << endl;
    return 0;
}

2. 搭配购买

题目描述

明天就是母亲节了,电脑组的小朋友们在忙碌的课业之余挖空心思想着该送什么礼物来表达自己的心意呢?听说在某个网站上有卖云朵的,小朋友们决定一同前往去看看这种神奇的商品,这个店里有 \(n\) 朵云,云朵已经被老板编号为 \(1,2,3,...,n\),并且每朵云都有一个价值,但是商店的老板是个很奇怪的人,他会告诉你一些云朵要搭配起来买才卖,也就是说买一朵云则与这朵云有搭配的云都要买,电脑组的你觉得这礼物实在是太新奇了,但是你的钱是有限的,所以你肯定是想用现有的钱买到尽量多价值的云。

输入格式

第一行输入三个整数,\(n,m,w\),表示有 \(n\) 朵云,\(m\) 个搭配和你现有的钱的数目。

第二行至 \(n+1\) 行,每行有两个整数, \(c_i,d_i\),表示第 \(i\) 朵云的价钱和价值。

第 \(n+2\) 至 \(n+1+m\) 行 ,每行有两个整数 \(u_i,v_i\)。表示买第 \(u_i\) 朵云就必须买第 \(v_i\) 朵云,同理,如果买第 \(v_i\) 朵就必须买第 \(u_i\) 朵。

输出格式

一行,表示可以获得的最大价值。

样例 #1

样例输入 #1

5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2

样例输出 #1

1

提示

  • 对于 \(30\%\) 的数据,满足 \(1 \le n \le 100\);
  • 对于 \(50\%\) 的数据,满足 \(1 \le n, w \le 10^3\),\(1 \le m \le 100\);
  • 对于 \(100\%\) 的数据,满足 \(1 \le n, w \le 10^4\),\(0 \le m \le 5 \times 10^3\)。

参考代码

//P1455
#include<stdio.h>
#include<iostream>
using namespace std;
int fa[10005], buy[10005], val[10005];
int f[10005];
bool flag[10005];
int find(int x){
    if(x == fa[x])      return x;
    return fa[x] = find(fa[x]);
}

void merge(int x, int y){
    buy[find(y)] += buy[find(x)];
    val[find(y)] += val[find(x)];
    fa[find(x)] = find(y);
}

int main(){
    int n, m, w;
    int u, v;
    cin >> n >> m >> w;
    for(int i = 1; i <= n; i++){
        cin >> buy[i] >> val[i];
        fa[i] = i;
    }
    for(int i = 1; i <= m; i++){
        cin >> u >> v;
        if(find(u) != find(v))      merge(u, v);
    }
    for(int i = 1; i <= n; i++){
        int ff = find(i);
        if(!flag[ff]){
            for(int j = w; j >= buy[ff]; j--){
                f[j] = max(f[j], f[j-buy[ff]] + val[ff]);
            }
            flag[ff] = 1;
        }
    }
    cout << f[w] << endl;
    return 0;
}

3. [USACO2.2] 集合 Subset Sums

题目描述

对于从 \(1\sim n\) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果 \(n=3\),对于 \(\{1,2,3\}\) 能划分成两个子集合,每个子集合的所有数字和是相等的:

\(\{3\}\) 和 \(\{1,2\}\) 是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数)
如果 \(n=7\),有四种方法能划分集合 \(\{1,2,3,4,5,6,7 \}\),每一种分法的子集合各数字和是相等的:

\(\{1,6,7\}\) 和 \(\{2,3,4,5\}\)
\(\{2,5,7\}\) 和 \(\{1,3,4,6\}\)
\(\{3,4,7\}\) 和 \(\{1,2,5,6\}\)
\(\{1,2,4,7\}\) 和 \(\{3,5,6\}\)

给出 \(n\),你的程序应该输出划分方案总数。

输入格式

输入文件只有一行,且只有一个整数 \(n\)

输出格式

输出划分方案总数。

样例 #1

样例输入 #1

7

样例输出 #1

4

提示

【数据范围】
对于 \(100\%\) 的数据,\(1\le n \le 39\)。

翻译来自NOCOW

USACO 2.2

参考代码

//P1466
//题意转换为
//1~n中选取k个数,其和为(1+n)*n/4的情况总数
#include<stdio.h>
#include<iostream>
using namespace std;
typedef long long ll;
ll f[50][2500];

int main(){
    int n, sum;
    cin >> n;
    if((1 + n) * n / 2 % 2 == 1){
        cout << 0 << endl;
        return 0;
    }
    sum = (1 + n) * n / 4;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= sum; j++){
            if(j <= i){
                f[i][j] = max(f[i][j], f[i-1][j]);
                if(j == i)  f[i][j] ++;
            }
            else    f[i][j] = f[i-1][j-i] + f[i-1][j];
        }
    }
    cout << f[n][sum] / 2 << endl;
    return 0;
}

4. 宝物筛选

题目描述

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 \(W\) 的采集车,洞穴里总共有 \(n\) 种宝物,每种宝物的价值为 \(v_i\),重量为 \(w_i\),每种宝物有 \(m_i\) 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为一个整数 \(n\) 和 \(W\),分别表示宝物种数和采集车的最大载重。

接下来 \(n\) 行每行三个整数 \(v_i,w_i,m_i\)。

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

样例 #1

样例输入 #1

4 20
3 9 3
5 9 1
9 4 2
8 1 3

样例输出 #1

47

提示

对于 \(30\%\) 的数据,\(n\leq \sum m_i\leq 10^4\),\(0\le W\leq 10^3\)。

对于 \(100\%\) 的数据,\(n\leq \sum m_i \leq 10^5\),\(0\le W\leq 4\times 10^4\),\(1\leq n\le 100\)。

#include<iostream>
using namespace std;
struct wp{
	int v, w, m;
}p[100005], er[1000];
int f[400005];
 
int main(){
	int n, w;
	int idx = 0;
	cin >> n >> w;
	for(int i = 1; i <= n; i++)		cin >> p[i].v >> p[i].w >> p[i].m;
	for(int i = 1; i <= n; i++){
		//将数量进行二进制处理
		idx ++; 
		er[idx].w = p[i].w;
		er[idx].v = p[i].v;
		int temp = 1,sum = 1;
		while(sum + temp * 2 <= p[i].m){
			idx ++;
			er[idx].w = er[idx-1].w*2;
			er[idx].v = er[idx-1].v*2;
			temp *= 2;
			sum += temp;
		}
		int cha = p[i].m - sum;
		if(cha > 0){
			idx ++;
			er[idx].w = p[i].w * cha;
			er[idx].v = p[i].v * cha;
		}
	}
	//转换成01背包问题
	for(int i = 1; i <= idx; i++){
		for(int j = w; j >= 0; j--){
			if(j >= er[i].w)	f[j] = max(f[j], f[j-er[i].w] + er[i].v);
		}
	}
	cout << f[w] << endl;
}

标签:多重,le,10,int,leq,背包,分组,宝物
From: https://www.cnblogs.com/hsy2093/p/18240035

相关文章

  • 4. 多重背包问题 I
    https://www.acwing.com/problem/content/4/有N种物品和一个容量是V的背包。第i种物品最多有si件,每件体积是vi,价值是wi。求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品种数......
  • GD32如何配置中断优先级分组以及中断优先级
    使用GD32MCU的过程中,大家可能会有以下疑问:中断优先级如何配置和使用?本文将会为大家解析中断优先级分组以及中断优先级的配置使用:中断优先级分组配置一个GD32MCU系统需要大家明确系统中使用的中断优先级分组,避免中断优先级配置越界导致一些不符合预期的中断现象。中断优先......
  • Excel数据透视表基础操作、组合字段、分组、创建新字段
    一、认识数据透视表作用:用于快速统计、汇总可以生成数据透视表的原表:采用流水账形式,有多行记录,明确区分字段属性(列)二、操作步骤1.创建数据透视表点击单元表范围内的某一格,在Excel上方菜单栏中选择插入-数据透视表,紧接着会自动识别用于生成数据透视表的区域。点击“确定......
  • teamcenter 按照审批节点和节点的目标分组统计任务数量
    selectcount(*),--(case--when'L8_DesignRevision'then'图对象'--when'L8_DocumentRevision'then'文档'--when'L8_JcsjDocumentRevision'then'检测数据'--WHENINSTR(v.pobject_type,'PartRevi......
  • 盘点一个Pandas数据分组的问题
    大家好,我是Python进阶者。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据分组的问题,问题如下:list1 = '电子税票号码 征收税务机关 社保经办机构 单位编号 费种 征收品目 征收子目 费款所属期 入(退)库日期 实缴(退)金额'list2 = list1.split(' ......
  • 回退背包专题
    P4141消失之物题目意思,就是说有n个物品,然后每个物品都有自己的体积w[i],然后问你,如果第i个物品丢了之后,还能够装满这个背包的方法,然后遍历一遍i同时也要遍历一遍背包,因为背包的值是在1到m之间的任意值,对于同一个物品丢失,中间结果不需要用加空格隔开,就是连在一起题解:这是一......
  • 49. 字母异位词分组
    题目给你一个字符串数组,请你将字母异位词组合在一起。可以按任意顺序返回结果列表。字母异位词是由重新排列源单词的所有字母得到的一个新单词。示例1:输入:strs=["eat","tea","tan","ate","nat","bat"]输出:[["bat"],["nat","tan"],[&qu......
  • 代码随想录算法训练营第四十六天|动态规划:完全背包理论基础、518.零钱兑换II、377. 组
    动态规划:完全背包理论基础文档讲解:代码随想录题目链接:52.携带研究材料(第七期模拟笔试)完全背包有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总......
  • 代码随想录算法训练营第四十九天| 139.单词拆分、多重背包
    139.单词拆分文档讲解:代码随想录题目链接:.-力扣(LeetCode)第一想法: 非空字符串s:背包非空单词的列表wordDict:物品每个物品可以使用多次,是一个完全背包问题看到这道题目的时候,大家应该回想起我们之前讲解回溯法专题的时候,讲过的一道题目回溯算法:分割回文串 (opens......
  • 背包 dp 学习笔记
    背包类问题是动态规划中的一类重要问题1.01背包有\(n\)件物品和一个容量为\(v\)的背包。第\(i\)件物品的费用是\(c_i\),价值是\(w_i\)。求解将哪些物品装入背包可使价值总和最大。1.1基本思路我们首先定义此问题的dp状态\(f_{i,j}\)表示前\(i\)件物品放入一个......