main
黄海 1 year ago
parent 1b290c795b
commit c85bea0aaf

@ -77,8 +77,26 @@ $3$个公司,$3$台机器,**机器都是一样的,一样的,记住,一
**答**:$1$号公司得到$1$台机器,$2$号公司得到$1$台机器,$3$号公司得到$1$台机器,就是$30+20+20=70$,此时国家利益最大。
### 二、分组背包
转换成 **分组背包问题** ,做 **等价变换**
<font color='blue' size=4><b>
① 第$i$个公司就是第$i$个分组
② 每个分组中可以一台也不要,可以要一台,可以要两台,...,可以要$S_i$台
</b></font>
**分组背包** 的 **闫氏DP分析法**
<center><img src='https://cdn.acwing.com/media/article/image/2021/06/21/55909_0a17f1d9d2-IMG_40E676992253-1.jpeg'></center>
初始状态 $f[0][0]$
目标状态 $f[N][M]$
### 三、不同$OJ$此题的差别
两者差别:
* $AcWing$<font color='red' size=4><b>答案不唯一,输出任意合法方案即可($Special$ $Judge$)</b></font>
* 洛谷: <font color='blue' size=4><b>$P.S.$要求答案的字典序最小</b></font>
@ -123,27 +141,6 @@ for (int k = 1; k <= j; k++) {
$k$的含义是当前分组中的物品个数,是由小到大的。而这里的计算式是$j-k$,这个东西在$k$前面的符号是负数,也就是$k$越小,值越大,$k$越大,值越小。按个循环逻辑,随着$k$的长大,就会枚举到更小的$j-k$,也就是枚举到更小的字典序。如果没有等号,就是$k$越来越大时,$j-k$越来越小,当价值一样时,越来越小的个数无法更新结果,反之,如果有等号,就是获取到字典序。
### 四、分组背包
转换成 **背包$DP$** 问题,需要对里面的一些叙述做出 **等价变换**
<font color='blue' size=4><b>
① 对于分给第$i$个公司的不同机器数量可以分别看作是一个物品组内的物品数量。
② 物品$k$的含义:分给第$i$个公司$k$台机器
③ 物品$k$的体积:因为一个机器算一个,所以体积也是$k$
④ 物品$k$的价值:$w_{k}$
</b></font>
直接上 **分组背包** 的 **闫氏DP分析法**
<center><img src='https://cdn.acwing.com/media/article/image/2021/06/21/55909_0a17f1d9d2-IMG_40E676992253-1.jpeg'></center>
初始状态 $f[0][0]$
目标状态 $f[N][M]$
**二维数组写法**
```cpp {.line-numbers}
@ -297,7 +294,7 @@ int main() {
```
### 四、深度优先搜索
数据范围比较小,$1<=N<=10,1<=M<=15$,把$m$个机器分配给$n$个公司,暴力遍历所有方案
数据范围比较小,$1 \leq N \leq 10,1 \leq M \leq 15$,把$m$个机器分配给$n$个公司,暴力遍历所有方案
记录分配方案,如果能更新最优解,顺便更新一下最优解的分配方案

@ -90,9 +90,10 @@ int main() {
cin >> v[i][j] >> w[i][j]; // 第i个分组中物品的体积和价值
}
for (int i = 1; i <= n; i++)
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= s[i]; k++)
for (int i = 1; i <= n; i++) // 每组
for (int j = 0; j <= m; j++) { // 每个合法体积
f[i][j] = f[i - 1][j]; // 如果一个都不要那么这一组就相当于白费给你机会也不中用继承于i-1
for (int k = 1; k <= s[i]; k++) // 选择第k个
if (j >= v[i][k])
f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]); // 枚举每一个PK一下大小
}
@ -100,6 +101,7 @@ int main() {
printf("%d", f[n][m]);
return 0;
}
```
### 四、一维数组版本

@ -623,6 +623,68 @@ int main() {
### 八、分组背包
**[$AcWing$ $9$. 分组背包问题](https://www.acwing.com/problem/content/description/9/)**
**二维状态**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int f[N][N], v[N][N], w[N][N], s[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i]; // 第i个分组中物品个数
for (int j = 1; j <= s[i]; j++)
cin >> v[i][j] >> w[i][j]; // 第i个分组中物品的体积和价值
}
for (int i = 1; i <= n; i++) // 每组
for (int j = 0; j <= m; j++) { // 每个合法体积
f[i][j] = f[i - 1][j]; // 如果一个都不要那么这一组就相当于白费给你机会也不中用继承于i-1
for (int k = 1; k <= s[i]; k++) // 选择第k个
if (j >= v[i][k])
f[i][j] = max(f[i][j], f[i - 1][j - v[i][k]] + w[i][k]); // 枚举每一个PK一下大小
}
// 输出打表结果
printf("%d", f[n][m]);
return 0;
}
```
**一维状态**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int v[N][N], w[N][N], s[N];
int f[N];
int main() {
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++)
if (j >= v[i][k])
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
printf("%d\n", f[m]);
return 0;
}
```
**[$AcWing$ $1013$. 机器分配](https://www.acwing.com/problem/content/1015/)**
分组背包求最优路径
Loading…
Cancel
Save