专栏文章
题解:P13008 【MX-X13-T3】「KDOI-12」只有失去光明,才能逃脱黑暗。
P13008题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mip0boqv
- 此快照首次捕获于
- 2025/12/03 04:04 3 个月前
- 此快照最后确认于
- 2025/12/03 04:04 3 个月前
题目描述
给定一个非负整数 ,你要经过若干次以下操作将其变成 ,求最小代价:
- 选择一个 ,花费 代价将 加或减 。
且操作时不需要保证 为非负整数。
算法思路
首先,令 ,很显然,从 变成 的过程相当于从 变成 的过程。
加或减 代表什么意思?可以将转化为二进制来解决。设一个二进制数的最右边一位是第 位,右边第二位是第 位,以此类推。所以加或减 就代表一个二进制数的第 位加或减 。
题目说可以花费 代价将一个二进制数的第 位加或减 ,但将第 位加或减 的最小代价是 吗?显然不是。可以设 为将第 位加或减 的最小代价,进行递推。以下是递推公式:
这个公式是怎么来的?
- 第 位只能花费 来加或减 。
- 对于 ,可以花费 ,也可以花费两个 ,两种中选小的那一个。
- 对于 ,只能花费两个 得到。
这是求 的代码:
CPPmina[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 来解决。我们要考虑是否向前进位。设 为解决 的 到 位,向 为进位为 的最小代价。其中 代表不进位, 代表进位。
枚举 的第 位和进位 。设 的这一位是
- 若 是偶数,则不用管,因为这一位加或减 ,相当于下一位 加或减 。令 。
- 若 是奇数,这一位既可以加 也可以减 ,同上面一种情况,加或减更多也不用考虑。所以令 ,。
dp 的代码:
CPPint 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]);
}
}
代码
CPP#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;
}
如果你觉得题解有帮助,请留一个小小的赞,谢谢!
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...