开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情
题目描述
规定两种数字操作:
- 加减操作,将数字加 或减 ,代价为 。
- 乘法操作,将数字乘 ,代价为 。
每种操作的使用次数不限。
请你计算,通过上述操作,将 变为 ,所需花费的最小代价。
输入共一行,包含三个整数 。
输出一个整数,表示最小代价。
前 55 个测试点满足 。
所有测试点满足 ,。
input1
8 1 1
output1
4
input2
8 1 10
output2
8
题目分析
这是一道 DP 的题目。
定义数组 表示从 到 的最小花费。
可以证明一种最优解方式为以下情况。
如果 是偶数,则 可以从 和 中较小值转移而来,可以证明不会从比 大的数 转移而来,代价分别为 ,。
如果 是奇数,则 可以从 和 中较小值转移而来, 则从 转移而来,代价分别为 ,。
注意 可以取到 , 会有爆 的可能,且需要计算题目所给的空间是否满足。
时间复杂度为 。
Accpet代码(DP)
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e7 + 10;
ll f[N];
int main()
{
int n, x, y;
cin >> n >> x >> y;
memset(f, 0x3f, sizeof f); // 0x3f3f3f为当前类型的足够大(非最大)的常量
f[0] = 0;
for (int i = 1; i <= n; i ++)
{
if (i % 2)
f[i] = min(f[i - 1] + x, f[(i + 1) / 2] + y + x);
else
f[i] = min(f[i - 1] + x, f[i / 2] + y);
}
cout << f[n];
return 0;
}
另一种牺牲空间时间代码较简洁的方法
本题 f[1] 的答案是显然的,以此为基本可以由两种方式的最小值求得 f[2],依次递归,最终求得答案,可以证明其的不漏性。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e7 + 10;
int n, x, y, f[N];
signed main()
{
cin >> n >> x >> y;
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i++)
{
f[i] = min(f[i], f[i - 1] + x);
f[i] = min(f[i], f[i + 1] + x);
f[i * 2] = f[i] + y;
}
cout << f[n];
return 0;
}