公式

D - Reconstruct Chocolate 解説 by KumaTachiRen


\(H\) ブロック、横 \(W\) ブロックのスペースがあり、そこにピースを並べていくものとします。

結論から述べると、縦幅か横幅が空いているスペースと一致するようなピースを空いているスペースの端に置くことを繰り返せばこの問題は解くことができます。

正当性について

縦 $H$ ブロック、横 $W$ ブロックの板チョコから問題文中の操作によって得られるようなピースの集合を $H\times W$ の良い分割 と呼ぶことにします。

  • $H=0$ または $W=0$ のとき、空集合は $H\times W$ の良い分割であるとします。

このとき空でない $H\times W$ の良い分割 $S$ に対して以下が成り立ちます。

  • (a). $S$ は縦 $H$ ブロックか横 $W$ ブロックのピースを含む。
  • (b). $S$ に含まれる縦 $H$ ブロック、横 $t$ ブロックのピースに対し、$S$ からそのピースを除いた集合は $H\times(W-t)$ の良い分割である。
  • (c). $S$ に含まれる縦 $t$ ブロック、横 $W$ ブロックのピースに対し、$S$ からそのピースを除いた集合は $(H-t)\times W$ の良い分割である。

証明:(a). 最初に机に置かれるピースを考えると明らかです。 (b). 縦 $H$ ブロックのピースが $k(\geq 1)$ 個存在するとき、それらは必ず最初の $k$ 回の操作で机に置かれており、その置かれる順序を入れ替えても最終的には同じピースの集合を作ることができます。したがって任意の縦 $H$ ブロックのピースについてそれを最初に机に置いたとみなすことができ、残りのピースは $H\times (W-t)$ の良い分割となります。よって (b) が従います。 (c). (b) と同様です。

(a),(b),(c) を用いれば上の解法が正しいことを帰納的に示すことができます。

実装においては残っているピースのうち縦幅が最大のピースおよび横幅が最大のピースを取得して空きスペースの幅と一致するなら置くことを繰り返せばよく、あらかじめ \(O(N\log N)\) 時間でピースを \(h_i\) の降順および \(w_i\) の降順で並び替えておきすでに確認したピースを飛ばすことで、並べる処理全体を \(O(N)\) 時間で行うことができます。

実装例 (C++)

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int H, W, N;
    cin >> H >> W >> N;
    vector<int> h(N), w(N);
    for (int i = 0; i < N; i++) cin >> h[i] >> w[i];

    // ord_h, ord_w : ピースの番号の並び替え
    // h[ord_h[i]] >= h[ord_h[i + 1]]
    // w[ord_w[i]] >= w[ord_w[i + 1]]
    vector<int> ord_h(N), ord_w(N);
    iota(ord_h.begin(), ord_h.end(), 0);
    sort(ord_h.begin(), ord_h.end(), [&](int x, int y) { return h[x] > h[y]; });
    iota(ord_w.begin(), ord_w.end(), 0);
    sort(ord_w.begin(), ord_w.end(), [&](int x, int y) { return w[x] > w[y]; });

    vector<int> ans_x(N, -1), ans_y(N, -1);
    vector<bool> used(N, false);
    auto ith = ord_h.begin();
    auto itw = ord_w.begin();
    for (int rem = N; rem > 0; rem--) {
        // すでに使用済みなら飛ばす
        while (used[*ith]) ith++;
        while (used[*itw]) itw++;
        // ピース i が縦 H ブロック or 横 W ブロック
        int i = h[*ith] == H ? *ith : *itw;
        // 右下に置く
        ans_x[i] = H - h[i] + 1;
        ans_y[i] = W - w[i] + 1;
        used[i] = true;
        if (h[i] == H) {
            W -= w[i];
        } else {
            H -= h[i];
        }
    }
    for (int i = 0; i < N; i++) cout << ans_x[i] << " " << ans_y[i] << "\n";
}

投稿日時:
最終更新: