题意
多测,一开始拥有一个全部为 1 1 1 的数组 a a a,以及输入两个长度一样数组 b b b 和 c c c。
可以进行 k k k 次操作:选择 a a a 数组中的一个数 a i a_i ai 和一个任意正整数 x x x,让 a i a_i ai 增加 ⌊ a i x ⌋ \left\lfloor\dfrac{a_i}{x}\right\rfloor ⌊xai⌋。
将 a i a_i ai 变为 b i b_i bi 的收益是 c i c_i ci,求 k k k 次以内的最大收益。
思路
这道题的
k
k
k 此操作看似唬人,实则为加上一个特殊的比自己小的数,考虑用 dp 来计算实现每个
b
i
b_i
bi 需要的费用
w
i
w_i
wi。
w
[
i
+
i
/
j
]
=
min
(
w
[
i
+
i
/
j
]
,
w
[
i
]
+
1
)
w[i + i / j] = \min(w[i + i / j], w[i] + 1)
w[i+i/j]=min(w[i+i/j],w[i]+1)
注意是用小推大,且变为
1
1
1 费用为
0
0
0。
然后问题发生变化,已知成功变换每个数的收益 c i c_i ci 和 费用 w i w_i wi,以及可以花费 k k k,求最大收益。
01 背包。
d p [ j ] = max ( d p [ j ] , d p [ j − w [ b [ i ] ] ] + c [ i ] ) dp[j] = \max(dp[j], dp[j - w[b[i]]] + c[i]) dp[j]=max(dp[j],dp[j−w[b[i]]]+c[i])
但代码复杂度为 O ( n k ) O(nk) O(nk),即 01 背包的复杂度,超时。
考虑优化,注意到每个数的最大花费不超过 13 13 13,即所有数全部变换完成也只需要 13 n 13n 13n 的费用, 13 n 13n 13n 的最大值为 13000 13000 13000,远小于 k k k,故可以在 k k k 和 13 n 13n 13n 之间取最小值作为背包的容量,时间复杂度为 O ( n 2 ) O(n^2) O(n2) 左右。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e3 + 10, M = 1e6 + 10, inf = 4e9;
int t, n, k;
int b[N], c[N], w[M];
int dp[M];
void work() {
memset(w, 0x3f, sizeof w);
w[1] = 0;
for(int i = 1;i <= 1000;i++) {
for(int j = 1;j <= i;j++) {
w[i + i / j] = min(w[i + i / j], w[i] + 1);
}
}
}
void _dp() { // 01 背包
memset(dp, 0, sizeof dp);
for(int i = 1;i <= n;i++) {
for(int j = k;j >= w[b[i]];j--) {
dp[j] = max(dp[j], dp[j - w[b[i]]] + c[i]);
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
work(); // 预处理每个数的花费
cin >> t;
while(t--) {
cin >> n >> k;
for(int i = 1;i <= n;i++) {
cin >> b[i];
}
for(int i = 1;i <= n;i++) {
cin >> c[i];
}
k = min(k, n * 13); // 优化
_dp();
cout << dp[k] << endl;
}
return 0;
}