ホームに戻る
ヤジリン ソルバー
ヤジリンというパズルを解くプログラムです。
入力の書式
H W N
Y1 X1 D1 C1
Y2 X2 D2 C2
...
入力の説明
H 行の数
W 列の数
N 数の数
Y 数の行数(0-index)
X 数の列数(0-index)
D 矢印の方向 U:上 R:右 D:下 L:左
C 数の数
入力例
6 6 3
1 2 R 0
2 5 L 3
5 0 R 0
やっていること
数を参考に黒マスの位置はすべて試します。
線の位置は再帰処理でバックトラックしています。
バックトラックの探索は以下の条件で引き返します。
・不正な黒マスの検出
・袋小路の検出
・線がたどり着けない領域の検出
正確に回答が得られない場合の条件
・盤面の数によるヒントがあまりにも少ない場合
・盤面に与えられる数が不正な場合
・正解が存在しない場合
注意すべき点
作成時点で10x10程度の盤面で正解を出力できました。
正解は同じ解であっても時計回りと反時計周りの2通りを出力します。
正確な解の個数を出力できるかの検証はしていません。
/******
*
* Yajirin Solver
*
*
******/
#include <algorithm>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
using namespace std;
typedef long long ll;
#define sz size()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define all(c) (c).begin(), (c).end()
#define rep(i,a,b) for(ll i=(a);i<(b);++i)
#define per(i,a,b) for(ll i=b-1LL;i>=(a);--i)
#define clr(a, b) memset((a), (b) ,sizeof(a))
#define ctos(c) string(1,c)
//#define print(x) cout<<#x<<" = "<<x<<endl;
#define MOD 1000000007
#define WHI 0 // 白マス
#define BLA 1 // 黒マス
#define NUM 2 // 数マス
#define SEK 4 // 線確定白マス
#define SEN 8 // 線マス
ll h,w,q;
ll sy,sx;
vector<pair<pair<ll,ll>, pair<ll,ll> > > vnl;
vector<vector<ll> > vv;
vector<vector<ll> > vans;
ll ans;
// 壁の判定
ll hit(ll y, ll x){
if(y<0)return 1;
if(x<0)return 1;
if(y>=h)return 1;
if(x>=w)return 1;
return 0;
}
// 表示
void display(){
string s = "URDL";
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==SEN&&vans[y][x]!=-1){
cout << s[vans[y][x]];
}
else{
cout << vv[y][x];
}
}
cout << endl;
}
cout << endl;
}
// 連続した黒マスのチェック
ll black_check(){
ll ret = 0;
rep(y,0,h){
rep(x,0,w){
if(y!=h-1&&(vv[y][x]==BLA&&vv[y+1][x]==BLA)){
ret = 1;
}
if(x!=w-1&&(vv[y][x]==BLA&&vv[y][x+1]==BLA)){
ret = 1;
}
}
}
return ret;
}
// 盤面の数と黒マスの数が適当かチェック
ll num_check(){
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
ll ret = 0;
rep(i,0,vnl.sz){
ll y,x,d,c;
y = vnl[i].fi.fi;
x = vnl[i].fi.se;
d = vnl[i].se.fi;
c = vnl[i].se.se;
ll count = 0;
while(1){
y += dy[d];
x += dx[d];
if(hit(y,x)==1)break;
if(BLA==vv[y][x])count++;
}
if(count!=c)ret = 1;
}
return ret;
}
// 完成しているかどうかをチェック
ll kansei_check(){
if(black_check()==1||num_check()==1)return 0;
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==SEK)return 0;
}
}
return 1;
}
// Union Find
vector<int> uf, usz;
int nc;
void init(int n){
vector<int> uf_(n);
vector<int> usz_(n, 1);
uf = uf_;
usz = usz_;
nc = n;
for(int i = 0; i < uf.size(); i++){
uf[i] = i;
}
}
int find(int a){
return (uf[a] == a) ? a : uf[a] = find(uf[a]);
}
void union_(int a, int b){
a = find(a);
b = find(b);
if(a != b){
if(usz[a] >= usz[b]){
swap(a, b);
}
uf[a] = b;
usz[b] += usz[a];
nc--;
}
}
int check(int a, int b){
return (find(a) == find(b)) ? 1 : 0;
}
int get_size(int a){
return usz[find(a)];
}
// 分断のチェック
ll bundan_check(){
init(h*w);
ll count = 0;
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==WHI||vv[y][x]==SEK)count++;
}
}
rep(y,0,h){
rep(x,0,w){
if(y!=h-1&&(vv[y][x]==WHI||vv[y][x]==SEK)&&(vv[y+1][x]==WHI||vv[y+1][x]==SEK)){
union_(y*w+x,(y+1)*w+x);
}
if(x!=w-1&&(vv[y][x]==WHI||vv[y][x]==SEK)&&(vv[y][x+1]==WHI||vv[y][x+1]==SEK)){
union_(y*w+x,y*w+x+1);
}
}
}
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==WHI||vv[y][x]==SEK){
if(get_size(y*w+x)!=count)return 1;
}
}
}
return 0;
}
// 線確定部分の検出
void senkakutei(stack<pair<pair<ll,ll>, ll> > &st){
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
// 数の条件から
rep(i,0,vnl.sz){
ll y,x,d,c;
y = vnl[i].fi.fi;
x = vnl[i].fi.se;
d = vnl[i].se.fi;
c = vnl[i].se.se;
vector<pair<ll,ll> > vp;
while(1){
y += dy[d];
x += dx[d];
if(hit(y,x)==1)break;
if(vv[y][x]==WHI){
st.push(mp(mp(y,x),vv[y][x]));
vv[y][x] = SEK;
}
}
}
// 黒マスの上下左右
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==BLA){
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
if(hit(y1,x1)==0){
if(vv[y1][x1]==WHI){
st.push(mp(mp(y1,x1),vv[y1][x1]));
vv[y1][x1] = SEK;
}
}
}
}
}
}
// 一方通行の前後
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==BLA){
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
ll y2 = y+dy[i]+dy[i];
ll x2 = x+dx[i]+dx[i];
if(hit(y1,x1)==0&&vv[y1][x1]==SEK){
ll y3 = y1 + dy[(i+1)%4];
ll x3 = x1 + dx[(i+1)%4];
ll y4 = y1 + dy[(i+3)%4];
ll x4 = x1 + dx[(i+3)%4];
if(hit(y2,x2)==1||(hit(y2,x2)==0&&(vv[y2][x2]==BLA||vv[y2][x2]==NUM))){
if(hit(y3,x3)==0&&vv[y3][x3]==WHI){
st.push(mp(mp(y3,x3),vv[y3][x3]));
vv[y3][x3] = SEK;
}
if(hit(y4,x4)==0&&vv[y4][x4]==WHI){
st.push(mp(mp(y4,x4),vv[y4][x4]));
vv[y4][x4] = SEK;
}
}
}
}
}
}
}
// 角の両側
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==BLA){
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
ll y2 = y+dy[i]*2;
ll x2 = x+dx[i]*2;
if(hit(y1,x1)==0){
ll y3 = y1 + dy[(i+1)%4];
ll x3 = x1 + dx[(i+1)%4];
ll y4 = y1 + dy[(i+3)%4];
ll x4 = x1 + dx[(i+3)%4];
if(hit(y3,x3)==1||(hit(y3,x3)==0&&(vv[y3][x3]==BLA||vv[y3][x3]==NUM))){
if(hit(y2,x2)==0&&vv[y2][x2]==WHI){
st.push(mp(mp(y2,x2),vv[y2][x2]));
vv[y2][x2] = SEK;
}
if(hit(y4,x4)==0&&vv[y4][x4]==WHI){
st.push(mp(mp(y4,x4),vv[y4][x4]));
vv[y4][x4] = SEK;
}
}
if(hit(y4,x4)==1||(hit(y4,x4)==0&&(vv[y4][x4]==BLA||vv[y4][x4]==NUM))){
if(hit(y2,x2)==0&&vv[y2][x2]==WHI){
st.push(mp(mp(y2,x2),vv[y2][x2]));
vv[y2][x2] = SEK;
}
if(hit(y3,x3)==0&&vv[y3][x3]==WHI){
st.push(mp(mp(y3,x3),vv[y3][x3]));
vv[y3][x3] = SEK;
}
}
}
}
}
}
}
}
// 袋小路の検出 黒マスで埋めて成り立つなら埋める
pair<ll,ll> fukurokoji1(ll yy, ll xx){
pair<ll,ll> p;
p.fi = -1;
p.se = -1;
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
rep(y,0,h){
rep(x,0,w){
if(y==yy&&x==xx)continue;
if(vv[y][x]!=WHI)continue;
ll count = 0;
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
if(y1==sy&&x1==sx)continue;
if(hit(y1,x1)==1)count++;
else{
if(vv[y1][x1]==NUM)count++;
if(vv[y1][x1]==SEN)count++;
}
}
if(count>=3){
p.fi = y;
p.se = x;
return p;
}
}
}
return p;
}
// 袋小路の検出 黒マスで埋められない場所が対象
ll fukurokoji2(ll yy, ll xx){
ll ret = 0;
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
rep(y,0,h){
rep(x,0,w){
if(y==yy&&x==xx)continue;
if(vv[y][x]!=WHI&&vv[y][x]!=SEK)continue;
ll count = 0;
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
if(y1==sy&&x1==sx)continue;
if(hit(y1,x1)==1)count++;
else{
if(vv[y1][x1]==BLA)count++;
if(vv[y1][x1]==NUM)count++;
if(vv[y1][x1]==SEN)count++;
}
}
if(count>=3){
ret = 1;
}
}
}
return ret;
}
// バックトラック本体
void f2(ll y, ll x, ll c){
if(hit(y,x)==1)return;
if(vv[y][x]==BLA)return;
if(vv[y][x]==NUM)return;
if(sy==y&&sx==x&&c>0){
stack<pair<pair<ll,ll>,ll> > st;
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==WHI){
st.push(mp(mp(y,x),vv[y][x]));
vv[y][x] = BLA;
}
}
}
if(kansei_check()==1){
ans++;
display();
}
while(!st.empty()){
pair<pair<ll,ll>, ll> pp = st.top();st.pop();
vv[pp.fi.fi][pp.fi.se] = pp.se;
}
return;
}
if(vv[y][x]==SEN)return;
if(bundan_check()==1)return;
stack<pair<pair<ll,ll>,ll> > st;
while(1){
pair<ll,ll> p = fukurokoji1(y,x);
ll y1 = p.fi;
ll x1 = p.se;
if(y1==-1)break;
st.push(mp(mp(y1,x1),vv[y1][x1]));
vv[y1][x1] = BLA;
}
if(fukurokoji2(y,x)==1||num_check()==1){
while(!st.empty()){
pair<pair<ll,ll>, ll> pp = st.top();st.pop();
vv[pp.fi.fi][pp.fi.se] = pp.se;
}
return;
}
st.push(mp(mp(y,x),vv[y][x]));
vv[y][x] = SEN;
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
rep(i,0,4){
ll y1 = y+dy[i];
ll x1 = x+dx[i];
vans[y][x] = i;
f2(y1,x1,c+1);
}
while(!st.empty()){
pair<pair<ll,ll>, ll> pp = st.top();st.pop();
vv[pp.fi.fi][pp.fi.se] = pp.se;
}
}
// 再帰のスタート位置を決定
void f1(){
ll y = 0;
ll x = 0;
rep(y,0,h){
rep(x,0,w){
if(vv[y][x]==SEK){
sy = y;
sx = x;
f2(y,x,0);
return;
}
}
}
}
// 解く
void solve(){
if(black_check()==1)return;
if(num_check()==1)return;
stack<pair<pair<ll,ll>, ll> > st;
senkakutei(st);
while(1){
pair<ll,ll> p = fukurokoji1(-1,-1);
ll y1 = p.fi;
ll x1 = p.se;
if(y1==-1)break;
st.push(mp(mp(y1,x1),vv[y1][x1]));
vv[y1][x1] = BLA;
}
if(fukurokoji2(-1,-1)==1||num_check()==1){
while(!st.empty()){
pair<pair<ll,ll>, ll> pp = st.top();st.pop();
vv[pp.fi.fi][pp.fi.se] = pp.se;
}
return;
}
f1();
while(!st.empty()){
pair<pair<ll,ll>, ll> pp = st.top();st.pop();
vv[pp.fi.fi][pp.fi.se] = pp.se;
}
}
// 数の条件から黒マスを全探索で埋める
void make_board(){
ll dy[4] = {-1,0,1,0};
ll dx[4] = {0,1,0,-1};
vector<vector<pair<ll,ll> > > vvp;
rep(i,0,vnl.sz){
ll y,x,d,c;
y = vnl[i].fi.fi;
x = vnl[i].fi.se;
vv[y][x] = NUM;
}
rep(i,0,vnl.sz){
ll y,x,d,c;
y = vnl[i].fi.fi;
x = vnl[i].fi.se;
d = vnl[i].se.fi;
c = vnl[i].se.se;
vector<pair<ll,ll> > vp;
while(1){
y += dy[d];
x += dx[d];
if(hit(y,x)==1)break;
if(NUM!=vv[y][x]){
vp.pb(mp(y,x));
}
}
vvp.pb(vp);
}
vector<vector<ll> > vv1;
rep(i,0,vvp.sz){
vector<ll> v;
ll a = vvp[i].sz;
ll c = vnl[i].se.se;
if(a-c<0)return;
rep(j,0,a-c)v.pb(0);
rep(j,0,c)v.pb(1);
vv1.pb(v);
}
ll flag = 0;
while(1){
ll index = 0;
while(1){
bool res = next_permutation(all(vv1[index]));
if(res==false){
sort(all(vv1[index]));
index++;
if(index==vv1.sz){
flag = 1;
break;
}
}
else break;
}
stack<pair<ll,ll> > st;
rep(i,0,vv1.sz){
rep(j,0,vv1[i].sz){
if(vv1[i][j]==1){
ll y = vvp[i][j].fi;
ll x = vvp[i][j].se;
st.push(mp(y,x));
vv[y][x] = BLA;
}
}
}
solve();
while(!st.empty()){
pair<ll,ll> p = st.top();st.pop();
vv[p.fi][p.se] = WHI;
}
if(flag==1)break;
}
}
int main(){
cin>>h>>w>>q;
rep(i,0,q){
ll y,x,d,c;
cin>>y>>x;
string s;
cin>>s;
if(s[0]=='U')d=0;
if(s[0]=='R')d=1;
if(s[0]=='D')d=2;
if(s[0]=='L')d=3;
cin>>c;
vnl.pb(mp(mp(y,x),mp(d,c)));
}
vector<vector<ll> > _vv(h,vector<ll>(w,WHI));
vv = _vv;
vector<vector<ll> > _vans(h,vector<ll>(w,-1));
vans = _vans;
make_board();
return 0;
}