思路:为了完整的用完(4^k - 1)/3 个L型骨牌,不妨采用四等分划分,对于没有特殊方格的区域构造一个L型骨牌,这样使得所有区域再放一下L型骨牌刚刚好把整个棋盘放满,然后再进行四等分递归划分,直到只有一个方格时才结束划分,应该算是一个经典的分治法吧。
代码:

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

const int maxn = 1e4 + 10;
int chess[maxn][maxn];
int cnt = 1;
void dfs(int sr,int sc,int len,int dx,int dy){
	if(len == 1)return; // 递归出口,即只有一个格子的时候 
	len /= 2; // 四等分划分即除二 
	cnt ++ ;//编号 
	if(dx < sr + len && dy < sc + len){ // 特殊方格在第一个区间 
		dfs(sr,sc,len,dx,dy); //无需放置L型骨牌,继续划分(len 已经改变) 
	}else{
		chess[sr + len -1][sc + len -1] = cnt;//特殊方格不在第一个区间,即需要放置L型骨牌 
		dfs(sr,sc,len,sr+len-1,sc+len-1);//放置完了后,当前区域的L型骨牌已经变成当前区域的特殊方格,继续划分(注意区域左上角和特殊方格的坐标变化) 
	}
	//下面的等价,不再赘述 
	if(dx < sr + len && dy >= sc + len){
		dfs(sr,sc + len,len,dx,dy);
	}else{
		chess[sr + len - 1][sc + len] = cnt;
		dfs(sr,sc + len,len,sr + len - 1,sc + len);
	}
	
	if(dx >= sr + len && dy < sc + len){
		dfs(sr + len,sc,len,dx,dy);
	}else{
		chess[sr + len][sc + len - 1] = cnt;
		dfs(sr + len,sc,len,sr + len,sc + len -1);
	}
	
	if(dx >= sr + len && dy >= sc + len){
		dfs(sr + len , sc + len ,len,dx,dy);
	}else{
		chess[sr + len][sc + len] = cnt;
		dfs(sr + len , sc + len , len ,sr + len, sc + len);
	}
}
int main(){
	srand(time(0));
	int k;cin>>k;
	int len = 1 << k;//2 ^ k
	int r = rand()%len + 1;//2 ^ k * 2 ^ k 方格种随机省生成一个特殊方格 
	int c = rand()%len + 1;
	chess[r][c] = 1;//标号 
	dfs(1,1,len,r,c);//递归划分 
	for(int i = 1; i <= len ; i++){
		for(int j = 1 ; j <= len ; j++){
			cout<<chess[i][j]<<" ";
		}
		cout<<endl;
	}
}