题解:P13008 【MX-X13-T3】「KDOI-12」只有失去光明,才能逃脱黑暗。
yezicong1104 · · 题解
题目描述
给定一个非负整数
- 选择一个
0 \le i \le k ,花费a_i 代价将x 加或减2^i 。
且操作时不需要保证
算法思路
首先,令
加或减
题目说可以花费
这个公式是怎么来的?
- 第
0 位只能花费a_i 来加或减1 。 - 对于
1 \ge i \ge k ,可以花费a_i ,也可以花费两个mina_{i-1} ,两种中选小的那一个。 - 对于
i>k ,只能花费两个mina_{i-1} 得到。
这是求
mina[0] = a[0];
for (int i = 1; i <= k; i++)
mina[i] = min(mina[i - 1] * 2, a[i]);
for (int i = k + 1; i <= 33; i++)
mina[i] = mina[i - 1] * 2;
接下来有一个简单的思路,我们从低到高来枚举
最小代价位
这个错误是因为每一位可以加
所以要改变思路,用 dp 来解决。我们要考虑是否向前进位。设
枚举
- 若
u+j 是偶数,则不用管,因为这一位加或减2k ,相当于下一位 加或减k 。令f(i+1,(u+j)/2)=\min(f(i+1,(u+j)/2),f(i,j)) 。 - 若
u+j 是奇数,这一位既可以加1 也可以减1 ,同上面一种情况,加或减更多也不用考虑。所以令f(i+1,0)=\min(f(i+1,0),f(i,j)+mina_i) ,f(i+1,1)=\min(f(i+1,1),f(i,j)+mina_i) 。
dp 的代码:
int d = abs(x - y), cnt = 0;
for (int i = d; i; i /= 2, cnt++);
for (int i = 0; i <= cnt; i++) {
int u = d >> i & 1;
f[i + 1][0] = f[i + 1][1] = INF;
for (int j = 0; j <= min(1, i); j++) {
if (u + j & 1) {
f[i + 1][0] = min(f[i + 1][0], f[i][j] + mina[i]);
f[i + 1][1] = min(f[i + 1][1], f[i][j] + mina[i]);
} else f[i + 1][u + j >> 1] = min(f[i + 1][u + j >> 1], f[i][j]);
}
}
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 40;
const LL INF = 1e18;
LL a[N], mina[N], f[N][2];
int main() {
int T;
scanf("%d", &T);
while (T--) {
int x, y, k;
scanf("%d%d%d", &x, &y, &k);
for (int i = 0; i <= k; i++)
scanf("%lld", &a[i]);
// 求mina
mina[0] = a[0];
for (int i = 1; i <= k; i++)
mina[i] = min(mina[i - 1] * 2, a[i]);
for (int i = k + 1; i <= 35; i++)
mina[i] = mina[i - 1] * 2;
// dp
int d = abs(x - y), cnt = 0;
for (int i = d; i; i /= 2, cnt++);
for (int i = 0; i <= cnt; i++) {
int u = d >> i & 1;
//初始化f
f[i + 1][0] = f[i + 1][1] = INF;
for (int j = 0; j <= min(1, i); j++) {
if (u + j & 1) {
f[i + 1][0] = min(f[i + 1][0], f[i][j] + mina[i]);
f[i + 1][1] = min(f[i + 1][1], f[i][j] + mina[i]);
} else f[i + 1][u + j >> 1] = min(f[i + 1][u + j >> 1], f[i][j]);
}
}
// 输出
printf("%lld\n", f[cnt + 1][0]);
}
return 0;
}
如果你觉得题解有帮助,请留一个小小的赞,谢谢!