高精度DP: A*B Problem

P2841 A*B Problem

这是一篇高精度 DP 的题解。

题目大意

给一个数 A,找出最小的 B 使得 AB 的十进制表示中只包含 01

DP 设计

一看到这个题,想到的是从低位到高位确定 B,因为 B 的高位无法影响 AB 的低位,积的低位和 B 的高位无关恰恰就是 DP 无后效性的原则。

设计状态 fi,j 表示已经确定了 B 最低的 i 位,记作 B,并且 B 满足 AB 最低的 i 位只有 01,还满足 AB10i=j 的最小的 B。转移很显然:

fi,j+kn10=min(j+kn)mod10<2fi1,j+k10i

转移的意义是在保证第 i 低位小于 2 的前提下,尽可能最小化 B. 每次 DP 完一个阶段 i, 寻找是否存在十进制表示是由 01 组成的 j, 满足 fi,j<

代码实现

需要用到的高精计算是:高精度加法,高精乘低精,高精度比较。写一个大整数类解决之。

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;
}
posted @   Wild_Donkey  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示