高精度DP: A*B Problem

P2841 A*B Problem

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

题目大意

给一个数 \(A\),找出最小的 \(B\) 使得 \(A*B\) 的十进制表示中只包含 \(0\)\(1\)

DP 设计

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

设计状态 \(f_{i, j}\) 表示已经确定了 \(B\) 最低的 \(i\) 位,记作 \(B'\),并且 \(B'\) 满足 \(A*B'\) 最低的 \(i\) 位只有 \(0\)\(1\),还满足 \(\lfloor \frac {A*B'}{10^i} \rfloor = j\) 的最小的 \(B'\)。转移很显然:

\[f_{i, \lfloor \frac {j + kn}{10}\rfloor} = \min_{(j + kn) \mod 10 < 2} f_{i - 1, j} + k10^i \]

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

代码实现

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

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 @ 2022-01-11 20:53  Wild_Donkey  阅读(57)  评论(0编辑  收藏  举报