高精度DP: A*B Problem
P2841 A*B Problem
这是一篇高精度 DP 的题解。
题目大意
给一个数 ,找出最小的 使得 的十进制表示中只包含 和 。
DP 设计
一看到这个题,想到的是从低位到高位确定 ,因为 的高位无法影响 的低位,积的低位和 的高位无关恰恰就是 DP 无后效性的原则。
设计状态 表示已经确定了 最低的 位,记作 ,并且 满足 最低的 位只有 和 ,还满足 的最小的 。转移很显然:
转移的意义是在保证第 低位小于 的前提下,尽可能最小化 . 每次 DP 完一个阶段 , 寻找是否存在十进制表示是由 和 组成的 , 满足 。
代码实现
需要用到的高精计算是:高精度加法,高精乘低精,高精度比较。写一个大整数类解决之。
struct BI {
unsigned Len;
char a[205];
inline BI() { Len = 1; a[0] = 0; }
inline void Prt() {
for (unsigned i(Len - 1); ~i; --i) putchar('0' + a[i]);
}
inline void Big() { Len = 200, a[199] = 1; }
inline void operator = (unsigned x) {
a[0] = 0;
for (Len = 1; x; x /= 10, ++Len) a[Len - 1] = x % 10;
if (Len > 1)--Len;
}
const inline char operator <(const BI& x) {
if (Len ^ x.Len) return Len < x.Len;
for (unsigned i(Len - 1); ~i; --i) if (a[i] ^ x.a[i]) return a[i] < x.a[i];
return 0;
}
inline BI operator +(const BI& x) {
BI Rt(x);
char Up(0);
for (unsigned i(max(Len, Rt.Len) + 1); i >= Rt.Len; --i) Rt.a[i] = 0;
Rt.Len = max(Len, Rt.Len) + 1;
for (unsigned i(0); i < Len; ++i) {
Rt.a[i] += a[i] + Up, Up = 0;
while (Rt.a[i] >= 10) Rt.a[i] -= 10, ++Up;
}
for (unsigned i(Len); i < Rt.Len && Up; ++i) {
Rt.a[i] += Up, Up = 0;
while (Rt.a[i] >= 10) Rt.a[i] -= 10, ++Up;
}
while (Up) {
Rt.a[Rt.Len] = Up, Up = 0;
while (a[Rt.Len] >= 10) Rt.a[Rt.Len] -= 10, ++Up;
++(Rt.Len);
}
while ((!(Rt.a[Rt.Len - 1])) && (Rt.Len > 1)) --(Rt.Len);
return Rt;
}
inline BI operator *(const unsigned& x) {
BI Rt(*this);
unsigned MuT(0);
for (unsigned i(Len); i <= 200; ++i) Rt.a[i] = 0;
for (unsigned i(0); i < Len; ++i) MuT += a[i] * x, Rt.a[i] = MuT % 10, MuT /= 10;
while (MuT) Rt.a[(Rt.Len)++] = MuT % 10, MuT /= 10;
return Rt;
}
}f[2][10005], Ten[205], Ans;
inline BI Suf(unsigned x, unsigned y) {
BI Rt;
Rt.Len = y + 1;
for (unsigned i(0); i <= y; ++i) Rt.a[i] = 0;
Rt.a[y] = x;
return Rt;
}
const unsigned Fin[2][10] = { {0, 9, 8, 7, 6, 5, 4, 3, 2, 1},{1, 0, 9, 8, 7, 6, 5, 4, 3, 2} };
unsigned D, t, n;
unsigned Cnt(0), Len(0);
set<unsigned long long> Tai[10];
bitset <10005> OK;
signed main() {
n = RD(), OK[0] = 1, Ans.Big();
for (unsigned i(1); i <= n; ++i) OK[i] = (OK[i / 10] & ((i % 10) < 2));
if (OK[n]) { printf("1 %u\n", n);return 0; }
for (unsigned i(0); i <= 200; ++i) Ten[i].Len = i + 1, Ten[i].a[i] = 1;
for (unsigned i(0); i < 10; ++i) Tai[(n * i) % 10].insert(i);
for (unsigned i(0); i <= n; ++i) f[0][i].Big();
for (auto i : Tai[0]) f[0][i * n / 10] = i;
for (auto i : Tai[1]) f[0][i * n / 10] = i;
while (1) {
for (unsigned i(1); i <= n; ++i) if (OK[i] & (f[Len & 1][i] < Ten[199])) if (f[Len & 1][i] < Ans) Ans = f[Len & 1][i];
if (Ans < Ten[199]) break;
++Len;
for (unsigned i(0); i <= n; ++i) f[Len & 1][i].Big();
for (unsigned i(0); i <= n; ++i) {
for (auto j : Tai[Fin[0][i % 10]]) {
unsigned Des((j * n + i) / 10);
BI Tmp(f[(Len & 1) ^ 1][i] + Suf(j, Len));
if (Tmp < f[Len & 1][Des]) f[Len & 1][Des] = Tmp;
}
for (auto j : Tai[Fin[1][i % 10]]) {
unsigned Des((j * n + i) / 10);
BI Tmp(f[(Len & 1) ^ 1][i] + Suf(j, Len));
if (Tmp < f[Len & 1][Des]) f[Len & 1][Des] = Tmp;
}
}
}
Ans.Prt(), putchar(' '), (Ans * n).Prt(), putchar(0x0A);
return Wild_Donkey;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具