圈乘运算问题
问题描述
定义 X@Y = 十进制整数 X 的各位数字之和 * 十进制整数 Y 的最大数字 + Y 的最小数字
对于给定的 X, K ,计算出由 X, @ 组成的表达式值为 K 的表达式最少需要多少个 @ 运算
算法设计
容易得放缩式 $$
\displaylines{X@Y \le 81 \times N + 9\\
where:\ N -1 < lg(X) \le N}
$$ 此式是容易证明的
注意到 N 代表着 X 的位数,则显然有十进制整数 X 的各位数字之和小于 9N ,又十进制中每一位最大值为 9 ,故上式得证。
当取最小位为 0 ,最大位为 1 时,同理可证明其下界为
X@Y ≥ X
定义 g(X, K) 为计算最小 @ 数量的函数,G[i] = g(X,i) 则可得其状态转移方程为
$$
\displaylines{G[i+j]=min\{G[i+j],\ G[i]+G[j]+1\}\\
where:\ X \le i,j \le K}
$$ 综上,参考cpp代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include <algorithm> #include <cstring> #include <iostream>
#define i32 int #define i64 long long #define u128 unsigned long long #define u32 unsigned int
using namespace std; const i32 MAXN = (int) 5e2;
const auto L = 0x3f;
i64 G[MAXN]; i64 X, K;
i64 f (i64 X, i64 Y) { i64 numX = 0, mxY = 0, mnY = 10; while (X) { numX += X % 10; X /= 10; } while (Y != 0) { mxY = max (mxY, Y % 10); mnY = min (mnY, Y % 10); Y /= 10; } return numX * mxY + mnY; }
i64 get_lim (i64 X, i64 K) { auto num = 0; while (X) { X /= 10; num++; } i64 lim = max (num, 2) * 81 + 9; return max(lim, K); }
int main () { memset (G, L, sizeof (G)); cin >> X >> K; auto lim = get_lim (X, K); G[f (X, X)] = 1; G[X] = 0;
if (X > K) { goto error; }
for (auto i = X; i <= lim; ++i) { for (auto j = X; j <= lim; ++j) { if (G[i] < L && G[j] < L && G[f (i, j)] > G[i] + G[j] + 1) { G[f (i, j)] = G[i] + G[j] + 1; } } }
if (G[K] < L) { cout << G[K] << endl; } else { error: cout << "error" << endl; } return 0; }
|