题目
题意:给定长度为
n
n
n的数组
b
b
b,
a
a
a,
a
a
a的初始值都为1,每次操作,可以选择数组
a
a
a的下标i和x,使得
a
i
=
a
i
+
⌊
a
i
/
x
⌋
a_i=a_i+\lfloor a_i/x \rfloor
ai=ai+⌊ai/x⌋。有
k
k
k操作,当操作后,所有
a
i
=
=
b
i
a_i==b_i
ai==bi的下标,可以获得硬币值
c
i
c_i
ci,问最多能得到多少硬币。
思路:预处理1到1000,计算从1到i的需要的最少步骤次数 m n mn mn;对于值, b i b_i bi,其需要的步骤次数为 m n [ b i ] mn[b_i] mn[bi]。问题转化为,给定代价 c o s t i cost_i costi,获取值 c o i n i coin_i coini,容量为 k k k。经典01背包。由于 k < = 1000000 , n < = 1000 k<=1000000,n<=1000 k<=1000000,n<=1000,但 m n mn mn最大值为12,复杂度为 n ∗ k = 12 n n*k=12n n∗k=12n。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1010;
int n, k;
int mn[maxn], mx, b;
int cost[maxn], coin[maxn];
int dp[maxn * 12];
void init() {
// 预处理,计算从1到i,需要的最少次数mn[i]
for (int i = 1; i <= 1000; ++i) {
mn[i] = 1000;
}
mn[1] = 0;
mx = 0;// mx = 12
for (int i = 1; i <= 1000; ++i) {
mx = max(mx, mn[i]);
for (int j = 1; j <= i; ++j) {
int v = i / j;
if (i + v > 1000) {
continue;
}
mn[i+v] = min(mn[i+v], mn[i] + 1);
}
}
// printf("mx %d \n", mx);
// for (int i = 1; i <= 20; ++i) {
// printf("%d ", i);
// }
}
void solve() {
scanf("%d%d", &n, &k);
int all = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", &b);
cost[i] = mn[b];
all += cost[i];
}
for (int i = 0; i < n; ++i) {
scanf("%d", &coin[i]);
}
// 01背包
k = min(k, all);
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; ++i) {
for (int j = k; j >= cost[i]; --j) {
dp[j] = max(dp[j-cost[i]] + coin[i], dp[j]);
}
}
printf("%d\n", dp[k]);
}
int main() {
int t;
init();
scanf("%d", &t);
while(t--) {
solve();
}
return 0;
}