Codeforces Global Round 1 D - Jongmah

Problem - D - Codeforces

概要

mm 以下の正整数が書かれた nn 個の牌がある。 ii 個目の牌には aia_i と書かれている。

このとき、以下の 2 種類の 3 枚組を合わせて最大何組作れるか求めよ。ただし 1 つの牌は 1 つの組にしか含めてはならない。

  • 刻子: 同じ数が書かれた牌 3 枚からなる組。
  • 順子: ある正整数 kk について、 (k,k+1,k+2)(k, k + 1, k + 2) が書かれた牌それぞれ 1 枚からなる組。

制約

  • 1n,m1061 \leq n, m \leq 10^6
  • 1aim1 \leq a_i \leq m

考察

数が小さい方から組み方を決める DP をしたくなるが、順子のせいで k2,k1k - 2, k - 1 の枚数まで状態として持たなければならない。

しかし実のところ、 kk を含む順子は 7 組以上組む必要がない。7 組以上ある場合、鳩の巣原理から (k2,k1,k),(k1,k,k+1),(k,k+1,k+2)(k - 2, k - 1, k), (k - 1, k, k + 1), (k, k + 1, k + 2) のいずれかの組は 3 組以上ある。しかし同じ順子 3 組は刻子 3 組に置き換えられる。

よって k2,k1k - 2, k - 1 の枚数は 6 枚以下のケースのみを考えればいい1。これで DP の状態数が 49m49 m に落ちて間に合う。

実装例

Submission #171293203 - Codeforces

 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
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;
constexpr int INF = 1 << 30;

int main() {
  int n, m;
  cin >> n >> m;

  // 最後にオーバーランするために、 m + 2 まで含める
  vector<int> cnt(m + 2, 0);
  while (n--) {
    int a;
    cin >> a;
    ++cnt[--a];  // 0-indexedに変換
  }

  // 前2つの枚数を持つ
  auto dp = vector(7, vector(7, -INF));
  dp[0][0] = 0;

  for (auto c : cnt) {
    auto ndp = vector(7, vector(7, -INF));

    for (int p = 0; p <= 6; ++p) {
      for (int q = 0; q <= 6; ++q) {
        // 以降のために残す枚数
        for (int r = 0; r <= min(c, 6); ++r) {
          for (int shuntsu = 0; shuntsu <= min({p, q, c - r}); ++shuntsu) {
            int kotsu = (c - r - shuntsu) / 3;  // 作れる刻子の数
            ndp[q - shuntsu][r] = max(ndp[q - shuntsu][r], dp[p][q] + shuntsu + kotsu);
          }
        }
      }
    }

    swap(dp, ndp);
  }

  cout << dp[0][0] << endl;
  return 0;
}

  1. 厳密には k2k - 2 は 2 枚以下、 k1k - 1 は 4 枚以下のケースのみを考えればいい。 ↩︎

Built with Hugo
テーマ StackJimmy によって設計されています。