Official

D - Tiling Editorial by en_translator


Let us denote by \((i,j)\) the cell in the \(i\)-th row from the top and \(j\)-th column from the left, and call the \(i\)-th tile simply tile \(i\).

First, notice that rotations are allowed by a multiple of \(90\) degrees, and there are one possible orientation of square tiles \((A_i=B_i)\) and two orientations of other tiles (\(A_i\times B_i\) or \(B_i\times A_i\)).

Next, we try to consider how to exhaustively search over the ways to put tiles. If there exists a conforming way to place tiles, whether it satisfies the conditions is independent of the order of placing tiles. So imposing the following additional constraints on placing tiles does not affect to the answer:

Let cell \(X\) be the first empty cell occurring in the sequence of cells enumerated in lexicographical order: \((1,1)\), \((1,2)\), \(\ldots\), \((1,W)\), \((2,1)\), \((2,2)\), \(\ldots\), \((N,N-1)\), \((N,N)\). (If there is no such tile, immediately terminate the procedure.) Next, choose a tile and its orientation, and place it so that it covers tile \(X\) (without sticking out from the grid or overlapping with other tiles).

Then, at each moment the cells on left and above of cell \(X\) have no cells (they are outside the grid) or has a tile on it. Thus, the only possible way to place a tile is to match (the top-left corner of) cell \(X\) to the top-left corner of the cell.
Since there are at most only two ways to place a tile, when filling the grid according to this algorithm, at each moment there are only \(2 \times\) (the number of unused tiles) ways to choose a tile and its orientation.

Therefore, if the order of using tiles and the orientation of each tile is determined, then the placement of tiles is unique. (If any tile sticks out of the grid or overlaps with others, then it is a failure; otherwise, the placement is unique.) Moreover, if there is a conforming way to place tiles, there always exists an order corresponding to it. This is because, for a conforming placement of tiles, one can always describe it as a sequence of tiles enumerated by the cells when inspecting the cells in lexicographical order, as well as their orientations.

Therefore, a conforming placement can be, if any, always found by exhaustively enumerating the order of using tiles and their orientations.

Here, the square \(X\) can be identified by \(HW\) inspections for each way of choosing tiles and orientations (throughout at most \(N\) enumeration for the way), so the total number of operations is \(N!\times 2^N\times HW\), which evaluates to at most \(6.5\times 10^7\) under the constraints of this problem, which is fast enough. The position of square \(X\) is dependent only on the tiles used so far and their orientations, so it can be found fast by recursive process. Also, one can apply pruning because it is no use once a tile sticks out of the grid or overlap with others. As a final note, unused tiles are allowed, so when searching for square \(X\), if it turns out that all the squares are covered, the answer is Yes.

Hence, the problem has been solved.

In fact, this problem has several approaches. Especially, \(N\), \(H\), and \(W\) are small, so brute-force approach is effective. Whatever the approach is, estimate the number of operations and check if it meets the time limit, and there is no problem.

Sample code in C++:

#include <bits/stdc++.h>

using namespace std;

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

#define MAX_N 7
#define MAX_H 10
#define MAX_W 10

int n,h,w;
int a[MAX_N];
int b[MAX_N];
int c[MAX_H][MAX_W];
bool ans;

void solve(int unused,int curi,int curj){
	bool can;
	while(c[curi][curj]>=0){
		curj++;
		if(curj>=w){curi++;curj=0;}
		if(curi>=h)break;
	}
	if(curi>=h){ans=true;return;}
	rep(i,n){
		if(unused&(1<<i)){
		  	can=true;
			rep(ii,a[i]){
				rep(jj,b[i]){
					if(((curi+ii)<h)&&((curj+jj)<w)){
						if(c[curi+ii][curj+jj]<0)c[curi+ii][curj+jj]=i;
						else {can=false;}
					}
					else  {can=false;}
				}
			}
			if(can)solve(unused^(1<<i),curi,curj);
			rep(ii,a[i]){
				rep(jj,b[i]){
					if(((curi+ii)<h)&&((curj+jj)<w)){
						if(c[curi+ii][curj+jj]==i)c[curi+ii][curj+jj]=-1;
					}
				}
			}
			if(a[i]!=b[i]){
				can=true;
				rep(ii,b[i]){
					rep(jj,a[i]){
						if(((curi+ii)<h)&&((curj+jj)<w)){
							if(c[curi+ii][curj+jj]<0)c[curi+ii][curj+jj]=i;
							else can=false;
						}
						else can=false;
					}
				}
				if(can)solve(unused^(1<<i),curi,curj);
				rep(ii,b[i]){
					rep(jj,a[i]){
						if(((curi+ii)<h)&&((curj+jj)<w)){
							if(c[curi+ii][curj+jj]==i)c[curi+ii][curj+jj]=-1;
						}
					}
				}

			}

		}
	}
	return;
}

int main(void){
	cin>>n>>h>>w;
	rep(i,n)cin>>a[i]>>b[i];
	rep(i,h)rep(j,w)c[i][j]=-1;
	ans=false;
	solve((1<<n)-1,0,0);
	if(ans)cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
}

posted:
last update: