ferinの競プロ帳

競プロについてのメモ

CODE FESTIVAL 2014 予選A D - 壊れた電卓

問題ページ
D: 壊れた電卓 - CODE FESTIVAL 2014 予選A | AtCoder

考えたこと

  1. 桁DPコンテストの問題として見たので桁DPを考える
  2. 使った数をbitで持ってi桁目までで差がもっとも小さい数を持つDPを考える
  3. dp[i桁目まで見た][使った数の集合]としてDPをする
  4. i桁目を決定するときA[i]との差がもっとも小さい数としたらサンプル通ったので投げると落ちる
  5. いろいろ試してるとi桁目の決定でおかしいパターンがあることに気づく
  6. i桁目までですでにAより小さいことが決定していれば使える最大の数、大きいことが決定していれば最小の数を使うべき
  7. dp[i桁目まで見た][使った数の集合][大小フラグ]としてDPして投げると通る

学び

#define __USE_MINGW_ANSI_STDIO 0
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef vector<ll> VL;
typedef vector<VL> VVL;
typedef pair<int, int> PII;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define IN(a, b, x) (a<=x&&x<b)
#define MP make_pair
#define PB push_back
#ifdef int
const ll INF = (1LL<<60);
#else
const int INF = (1LL<<30);
#endif
const double PI = 3.14159265359;
const double EPS = 1e-12;
const int MOD = 1000000007;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

int dp[20][1LL<<10][3];
signed main(void)
{
  int A;
  int K;
  cin >> A >> K;
  string a = to_string(A);
  int n = a.size();

  memset(dp, -1, sizeof(dp));
  dp[0][0][0] = 0;
  REP(i, n) {
    REP(j, 1<<10) REP(k, 3) {
      if(dp[i][j][k] == -1) continue;
      REP(d, 10) {
        int tmp = j;
        if(!(j & (1<<d))) tmp |= 1<<d;
        // tmpで桁が1の中からどれを選ぶのが最善か?
        if(k == 0) {
          if(tmp & 1<<(a[i]-'0')) dp[i+1][tmp][0] = dp[i][j][k]*10 + a[i]-'0';
          REP(l, a[i]-'0') if(tmp & 1<<l) {
            chmax(dp[i+1][tmp][1], dp[i][j][k]*10 + l);
          }
          for(int l=9; l>a[i]-'0'; --l) if(tmp & 1<<l) {
            if(dp[i+1][tmp][2] == -1) dp[i+1][tmp][2] = dp[i][j][k]*10 + l;
            chmin(dp[i+1][tmp][2], dp[i][j][k]*10 + l);
          }
        }
        // 小さいのが確定
        else if(k == 1) {
          REP(l, 10) if(tmp & 1<<l) {
            chmax(dp[i+1][tmp][1], dp[i][j][k]*10 + l);
          }
        }
        // 大きいのが確定
        else if(k == 2) {
          for(int l=9; l>=0; --l) {
            if(tmp & 1<<l) {
              if(dp[i+1][tmp][2] == -1) dp[i+1][tmp][2] = dp[i][j][k]*10 + l;
              chmin(dp[i+1][tmp][2], dp[i][j][k] * 10 + l);
            }
          }
        }
      }
    }
  }

  int ans = INF;
  REP(i, 1<<10) REP(k, 3) {
    int cnt = 0;
    REP(j, 10) if(i & (1<<j)) cnt++;
    if(cnt <= K) {
      if(dp[n][i][k] != -1 && abs(ans-A) > abs(dp[n][i][k]-A)) {
        ans = dp[n][i][k];
      }
    }
  }
  int r = 0;
  REP(i, n-1) r *= 10, r += 9;
  cout << min(abs(A-ans), abs(A-r)) << endl;

  return 0;
}