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 }

浙公网安备 33010602011771号