公式
D - ABC Puzzle 解説
by
D - ABC Puzzle 解説
by
physics0523
実は、「 各行 / 各列 に A
, B
, C
がちょうど
1 個ずつ含まれる 」を満たすグリッドは \(N=5\) の場合でも \(66240\) 個しか存在しません。
なので、様々な方針の枝刈り全探索でこの問題に正解することができます。この解説ではその一例を示します。
以下、 \(N=5\) であったとして説明します。
まず、行の制約のみに着目します。このとき、以下の \(2\) つの条件を守ることになります。
- 全ての行は
ABC..
の並べ替えである - 上から \(i\) 行目の最も左にある
.
以外の文字は \(R_i\) である
各行についてこれを満たすものは \(20\) 通りしかありません。 ( \((5 \times 4 \times 3 )/ 3 = 20\) )
これを \(5\) 行分試すことを考えます (合計 \(20^5\) 通り)
上の行から順に決定していくことを考えます。このとき、以下の条件に違反してはなりません。
- 各列は
A
,B
,C
を高々 \(1\) つずつ含む。 - 左から \(j\) 列目の最も上にある
.
以外の文字は \(C_j\) である
これに違反したことが発覚した時点で、そこで探索を打ち切って構いません。
そうして \(N\) 行全てが決まった場合、最後に以下をチェックします。
- 各行は
A
,B
,C
を丁度 \(1\) つ含む。
この条件も満たしていれば、そのグリッドは問題文中の条件を全て満たしています。
この解法中で演算を行う回数は高々 \(20^5 \times 5^2 = 8 \times 10^7 \times \) ( 小さな定数倍 ) 回程度と見積もれますが、実際は非常に高速に動作します。
実装例 (C++):
#include<bits/stdc++.h>
using namespace std;
vector<vector<string>> row(3);
bool fnd=false;
int n;
string r,c;
vector<string> grid;
void dfs(int i,int fl){
if(fnd){return;}
if(i==n){
if(fl==0){
cout << "Yes\n";
for(auto &nx : grid){cout << nx << "\n";}
fnd=true;
}
return;
}
for(auto &nx : row[r[i]-'A']){
grid.push_back(nx);
int ufl=fl;
bool und=true;
for(int j=0;j<n;j++){
if(nx[j]=='.'){continue;}
int kind=(nx[j]-'A');
if((fl&(1<<(4*j+kind)))==0){und=false;break;}
ufl^=(1<<(4*j+kind));
if((fl&(1<<(4*j+3)))!=0){
if(nx[j]!=c[j]){und=false;break;}
ufl^=(1<<(4*j+3));
}
}
if(und){dfs(i+1,ufl);}
grid.pop_back();
}
}
int main(){
cin >> n >> r >> c;
string abc="ABC";
while(abc.size()<n){abc.push_back('.');}
sort(abc.begin(),abc.end());
do{
char tg='.';
for(auto &nx : abc){
if(nx!='.'){tg=nx;break;}
}
row[tg-'A'].push_back(abc);
}while(next_permutation(abc.begin(),abc.end()));
dfs(0,(1<<(4*n))-1);
if(!fnd){cout << "No\n";}
return 0;
}
投稿日時:
最終更新: