Official

D - Polyomino Editorial by en_translator


We think that the hardness of the implementation of this problem largely depends on the choice of approaches. If you had trouble in implementing it, we recommend you seek for a better way reading this article and other contestant’s solutions.

Since the constraints of this problem are small, we do not have to use an advanced algorithm, but only exhaustively enumerate all possible placements. Specifically, we can determine every element one-by-one as follows:

  • Fix the orientation of the first polyomino, and enumerate all possible \(4 \times 4 = 16\) orientations of the second and third.
    • Enumerate all position of the first polyomino.
      • Enumerate all position of the second polyomino.
        • Enumerate all position of the third polyomino.
  • Print Yes if a conforming placement is found, and No otherwise.

By properly implementing this algorithm, the problem can be solved.
The most difficult part of this algorithm may be enumerate all possible rotations of a polyomino, so we describe in detail how to do so. (We think that most people are relatively used to placing them, as there have been similar problems like ABC307-C.)

Since this problem allows rotation of a polyomino, we have to brute-force all possible orientations of each polyomino. Since the grid is square-shaped, we can assume that the first polyomino are not rotated, so the rotation of the first is skipped, but still we need to inspect \(4 \times 4 = 16\) cases of orientations of the second and third.
This part may require a bunch of variables depending on approaches, which can bother; we introduce typical implementations in such problems.

First, let us extract the rotation of polyomino as a function:

#define rep(i, n) for (int i = 0; i < (n); i++)

// Rotate the polyomino clockwise by 90 degrees
void Rotate(vector<string>& v) {
  vector<string> w = v;
  rep(i, 4) rep(j, 4) w[3 - j][i] = v[i][j];
  v = w;
}

Then, write the following double loop.

// Suppose that polyominoes are stored in ps[0], ps[1], and ps[2]
rep(t, 4) {
  rep(u, 4) {
    // check : a function that determines whether the polyominoes fit in the current orientations
    check();
    // rotate ps[2] by 90 degrees
    Rotate(ps[2]);
  }
  // rotate ps[1] by 90 degrees
  Rotate(ps[1]);
}

In fact, this double loop inspects all \(4 \times 4 = 16\) cases. Specifically, the state where the second polyomino are rotated \(a\) times and third \(b\) times are inspected when the loop variables of the for statements are \(t = a\) and \(u = b\).
The technique of exhaustive search with rotations of variables is a bit technical but has a wide range of applications (especially when the reverse of a sequence matters), so we recommend you to learn it.

The sample code for the entire problem is as follows.

  • Sample code (C++)
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#define rep(i, n) for (int i = 0; i < (n); i++)

// Print `s` and terminate the program
void Print(const string& s) {
  cout << s << "\n";
  exit(0);
}

// Rotate clockwise by 90 degrees
void Rotate(vector<string>& v) {
  vector<string> w = v;
  rep(i, 4) rep(j, 4) w[3 - j][i] = v[i][j];
  v = w;
}

// Determines if (i, j) is within the grid
bool in(int i, int j) { return 0 <= i and i < 4 and 0 <= j and j < 4; }

// Can we put p onto the grid, so that the top-left position is (di, dj)?
bool can_put(vector<vector<int>>& exist, const vector<string>& p, int di,
             int dj) {
  rep(i, 4) rep(j, 4) {
    if (p[i][j] == '#') {
      int ni = i + di;
      int nj = j + dj;
      if (!in(ni, nj)) return false;
      if (exist[ni][nj] == 1) return false;
      exist[ni][nj] = 1;
    }
  }
  return true;
}

// A recursive function that places polyominoes
void dfs(int i, const vector<vector<int>>& exist,
         const vector<vector<string>>& ps) {
  if (i == 3) {
    int ok = 1;
    rep(u, 4) rep(v, 4) ok &= exist[u][v];
    if (ok) Print("Yes");
    return;
  }
  for (int di = -3; di <= 3; di++) {
    for (int dj = -3; dj <= 3; dj++) {
      auto ex2 = exist;
      bool flag = can_put(ex2, ps[i], di, dj);
      if (flag) dfs(i + 1, ex2, ps);
    }
  }
}

int main() {
  vector<vector<string>> ps(3);
  rep(i, 3) {
    ps[i].resize(4);
    for (auto& s : ps[i]) cin >> s;
  }
  rep(t, 4) {
    rep(u, 4) {
      dfs(0, vector(4, vector(4, 0)), ps);
      Rotate(ps[2]);
    }
    Rotate(ps[1]);
  }
  Print("No");
}

posted:
last update: