E - Make Them Equal题解(cf原题类似01背包)

在讲这个题之前先讲一遍 01 背包吧;

在一些例题之中,每一个物品只有两种状态:拿或者不拿,可以对应二进制中的 0 和 1,所以我们称呼这种问题为 01 背包问题。

在这类问题中我们会知道第 i 个物品的重量 wi 和价值 vi ,以及背包的容量 W, 我们需要求得将 n 个物品放入背包里面,在背包容量允许下能获取的最大价值。

我们设 dp 状态 f ( i,  j ) 为只能放前 i 个物品时,容量为 j 的背包能获取的最大总价值。

假如说我们已经处理了 i - 1 个物品,那么第 i 个物品如果不拿,背包的剩余容量不会改变,背包中物品的价值也不会改变,所以说这个时候的最大价值应该为:f ( i - 1, j ) ;

那么如果当前物品放入背包时,背包的容量会减小 wi ,而他的价值会增加 vi,所以这个时候的最大价值为:f ( i - 1, j - wi ) + vi 。

那么此时我们就可以明确 dp 状态方程 : f ( i , j ) = max( f ( i - 1,  j ) ,  f ( i - 1, j - wi ) + vi );

而此时的状态方程可以用滚动数组进行优化将其变成一维的形式:f ( i ) 表示当前背包容量为 i 时的最大价值。

dp 状态方程就成了:f ( i ) = max ( f ( i ) , f ( i - wi ) + vi ) ;

这里附上一个01背包板子:

1 for (int i = 1; i <= n; i++)//01板子
2     for (int l = W; l >= w[i]; l--) 
3         f[l] = max(f[l], f[l - w[i]] + v[i]);
 

 我们再来看题目

[题目传送门](https://codeforces.com/problemset/problem/1633/D)

题目大意:给你一个只由 1 构成的数组 a,有两个数组 b , c, 如果将ai经过一系列操作后变成 bi,

即可获得ci的得分,已知可以操作k次,求最大分数

每次操作只能是 ai + ai / x(x为整数)。

这里我们假设 ai 到 bi 的步数为 mi, 就可以理解成01背包问题了,我需要消耗 m 次操作数换来相应的
分数 ci, 最大操作数为 k 。

这里我们来观察一下,mi 可以对应 01 背包里面的 wi , ci 可以对应 01 背包里面的 vi , 而 k 值可以对应 01 背包里面的 W 容量;

那如果我们知道了 mi 的值,是不是就可以直接套用 01 背包了。

然后的思路就是想办法去找到 ai 到 bi 所对应的 mi 值,可以跑个 bfs 然后预处理一下;

我的是暴力跑循环QAQ;

这里的 g [ i ] 表示 1 到 i 的最少操作步数,我在这里采用了暴力的方法去循环求得 g [ i ]  的最小值。

 1 for (int i = 1; i <= N; i++)//通过循环求得 1 到 N 每一个常数所需要的操作数
 2     {
 3         for (int j = 1; j <= i; j++)//这里 j 就是上面操作公式中对 X 整数的枚举
 4         {
 5             if (i + i / j <= N)
 6             {
 7                 g[i + i / j] = min(g[i + i / j], g[i] + 1);//通过枚举各种情况去求 g[i] 的最小值
 8             }
 9         }
10     }

**需要注意1到1的操作数是 0, 所以 g [ 1 ] = 0 ; 

下面是完整代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <vector>
 5 #include <cmath>
 6 #include <cstring>
 7 #include <queue>
 8 #include <map>
 9 #include <unordered_map>
10 #include <set>
11 #define ll long long
12 #define ull unsigned long long
13 #define ci const int
14 #define PLL pair<ll, ll>
15 #define PII pair<int, int>
16 #define PSI pair<string, int>
17 #define MAX1 0x3f3f3f3f
18 #define lowbit(x) x &(-x)
19 using namespace std;
20 ci N = 1e3 + 7;
21 int g[N];
22 void init()//预处理初始化
23 {
24     memset(g, MAX1, sizeof g);
25     g[1] = 0;
26     for (int i = 1; i <= N; i++)//通过循环找到 1 到 N 之间每一个常数的最小操作步数
27     {
28         for (int j = 1; j <= i; j++)
29         {
30             if (i + i / j <= N)
31             {
32                 g[i + i / j] = min(g[i + i / j], g[i] + 1);
33             }
34         }
35     }
36 }
37 int main()
38 {
39     init();
40     ios::sync_with_stdio(false);
41     int t;
42     cin >> t;
43     while (t--)
44     {
45         int n, k;
46         cin >> n >> k;
47         vector<int> b(n + 1), c(n + 1);
48         for (int i = 1; i <= n; i++)
49             cin >> b[i], b[i] = g[b[i]];//因为之前预处理过,所以这里就可以之间将 bi 更新成 mi
50         for (int i = 1; i <= n; i++)
51             cin >> c[i];
52         vector<int> f(k + 1);
53         for (int i = 1; i <= n; i++)//然后就是 01 背包问题套板子了
54         {
55             for(int j = k; j >= b[i]; j--)
56             {
57                 f[j] = max(f[j], f[j - b[i]] + c[i]);
58             }
59         }
60         cout << f[k] << "\n";
61     }
62     return 0;
63 }

 

posted @ 2022-07-05 14:36  aYi_7  阅读(223)  评论(0)    收藏  举报