比较恶心的搜索题。
首先翻转操作至多用一次,且只有用和不用的区别,于是可以枚举是否使用。可以发现行和列的排列是无关的,且分别有
6
4
6^4
64种。而数字的交换相当于允许任意排列,可以考虑最小表示法。
这样一个算法就很明显了,我们直接对于每个数独,枚举它是否进行翻转操作,以及行和列的排列,最后求出得到的新数独的最小表示尝试匹配。
但是这样复杂度有点高。由于操作是可逆的,容易发现可以用meet in middle来优化,对于一个数独我们枚举它是否进行翻转以及行的排列,得到一个中间状态,对于另一个数独我们枚举它做列的排列,尝试匹配。
这里需要输出方案,可以考虑记录每个状态所用的排列。
使用哈希的话,时间复杂度为
O
(
n
⋅
9
2
⋅
6
4
)
\mathcal O(n\cdot 9^2\cdot 6^4)
O(n⋅92⋅64)。
#include <bits/stdc++.h>
#define FR first
#define SE second
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
mt19937 rnd(19260817);
inline int getrnd() {
return abs((ll)rnd())%(1<<20);
}
const int perm[6][3]={
{1,2,3},
{1,3,2},
{2,3,1},
{2,1,3},
{3,1,2},
{3,2,1}
};
int p[1500][10],bel[1500][4],sz;
int w[81][10];
void pre() {
for(int t1=0;t1<6;t1++)
for(int t2=0;t2<6;t2++)
for(int t3=0;t3<6;t3++)
for(int t4=0;t4<6;t4++) {
sz++;
bel[sz][0]=t1;
bel[sz][1]=t2;
bel[sz][2]=t3;
bel[sz][3]=t4;
p[sz][1]=(perm[t1][0]-1)*3+perm[t2][0];
p[sz][2]=(perm[t1][0]-1)*3+perm[t2][1];
p[sz][3]=(perm[t1][0]-1)*3+perm[t2][2];
p[sz][4]=(perm[t1][1]-1)*3+perm[t3][0];
p[sz][5]=(perm[t1][1]-1)*3+perm[t3][1];
p[sz][6]=(perm[t1][1]-1)*3+perm[t3][2];
p[sz][7]=(perm[t1][2]-1)*3+perm[t4][0];
p[sz][8]=(perm[t1][2]-1)*3+perm[t4][1];
p[sz][9]=(perm[t1][2]-1)*3+perm[t4][2];
}
for(int i=0;i<81;i++)
for(int j=0;j<10;j++) w[i][j]=getrnd();
}
struct State {
vector<int> vt;
int id,p,kind,val[10];
State() {}
};
State a[1000005];
namespace Hash {
int head[1<<20],tot;
int num[1000005],nxt[1000005];
void insert(int x) {
int v=0;
for(int i=0;i<81;i++) v^=w[i][a[x].vt[i]];
num[++tot]=x;
nxt[tot]=head[v];
head[v]=tot;
}
}
char str[25][11][11];
void search(int n) {
int tot=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=sz;j++) {
int vis[10],cnt;
memset(vis,0,sizeof(vis));
a[++tot].id=i;
a[tot].p=j;
a[tot].kind=0;
cnt=0;
for(int k=1;k<=9;k++)
for(int l=1;l<=9;l++) {
char v=str[i][p[j][k]][l];
if (v=='.') a[tot].vt.push_back(0);
else {
v-='0';
if (!vis[v]) vis[v]=++cnt;
a[tot].vt.push_back(vis[v]);
}
}
for(int k=1;k<=9;k++) {
if (!vis[k]) vis[k]=++cnt;
a[tot].val[k]=vis[k];
}
Hash::insert(tot);
memset(vis,0,sizeof(vis));
a[++tot].id=i;
a[tot].p=j;
a[tot].kind=1;
cnt=0;
for(int k=1;k<=9;k++)
for(int l=1;l<=9;l++) {
char v=str[i][l][p[j][k]];
if (v=='.') a[tot].vt.push_back(0);
else {
v-='0';
if (!vis[v]) vis[v]=++cnt;
a[tot].vt.push_back(vis[v]);
}
}
for(int k=1;k<=9;k++) {
if (!vis[k]) vis[k]=++cnt;
a[tot].val[k]=vis[k];
}
Hash::insert(tot);
memset(vis,0,sizeof(vis));
a[++tot].id=i;
a[tot].p=j;
a[tot].kind=2;
cnt=0;
for(int k=1;k<=9;k++)
for(int l=1;l<=9;l++) {
char v=str[i][k][p[j][l]];
if (v=='.') a[tot].vt.push_back(0);
else {
v-='0';
if (!vis[v]) vis[v]=++cnt;
a[tot].vt.push_back(vis[v]);
}
}
for(int k=1;k<=9;k++) {
if (!vis[k]) vis[k]=++cnt;
a[tot].val[k]=vis[k];
}
Hash::insert(tot);
}
}
pr ans[25][25];
vector <int> vt[25];
int match[25];
void solve(int n) {
for(int i=0;i<(1<<20);i++)
if (Hash::head[i]) {
int st=0;
for(int j=1;j<=n;j++) vt[j].clear();
for(int j=Hash::head[i];j;j=Hash::nxt[j])
if (a[Hash::num[j]].kind==2) {
int x=a[Hash::num[j]].id;
vt[x].push_back(Hash::num[j]);
st|=(1<<x);
}
for(int j=Hash::head[i];j;j=Hash::nxt[j])
if (a[Hash::num[j]].kind<2) {
int x=a[Hash::num[j]].id;
for(int k=x+1;k<=n;k++)
if (!((match[x]>>k)&1)&&((st>>k)&1)) {
for(int l=0;l<vt[k].size();l++)
if (a[Hash::num[j]].vt==a[vt[k][l]].vt) {
match[x]|=(1<<k);
ans[x][k]=pr(Hash::num[j],vt[k][l]);
break;
}
}
}
}
}
vector<pr> query(int x) {
vector<pr> vt;
if (x==1) vt.push_back(pr(2,3));
else if (x==2) {
vt.push_back(pr(1,2));
vt.push_back(pr(2,3));
}
else if (x==3) vt.push_back(pr(1,2));
else if (x==4) {
vt.push_back(pr(1,2));
vt.push_back(pr(1,3));
}
else if (x==5) vt.push_back(pr(1,3));
return vt;
}
struct Ope {
char ch;
int x,y;
Ope(char a,int b,int c):ch(a),x(b),y(c) {}
};
void output(int x,int y) {
int u=ans[x][y].FR,v=ans[x][y].SE;
if (!u) {
puts("No");
return;
}
vector<pr> cur;
bool vis[10];
vector<Ope> vt1;
cur=query(bel[a[u].p][0]);
for(int i=0;i<cur.size();i++) vt1.push_back(Ope('R',cur[i].FR,cur[i].SE));
cur=query(bel[a[u].p][1]);
for(int i=0;i<cur.size();i++) vt1.push_back(Ope('r',cur[i].FR,cur[i].SE));
cur=query(bel[a[u].p][2]);
for(int i=0;i<cur.size();i++) vt1.push_back(Ope('r',cur[i].FR+3,cur[i].SE+3));
cur=query(bel[a[u].p][3]);
for(int i=0;i<cur.size();i++) vt1.push_back(Ope('r',cur[i].FR+6,cur[i].SE+6));
memset(vis,0,sizeof(vis));
for(int i=1;i<=9;i++)
if (!vis[i]) {
int x=i;
do {
vis[x]=1;
if (!vis[a[u].val[x]]) vt1.push_back(Ope('D',i,a[u].val[x]));
x=a[u].val[x];
} while (!vis[x]);
}
vector<Ope> vt2;
cur=query(bel[a[v].p][0]);
for(int i=0;i<cur.size();i++) vt2.push_back(Ope('C',cur[i].FR,cur[i].SE));
cur=query(bel[a[v].p][1]);
for(int i=0;i<cur.size();i++) vt2.push_back(Ope('c',cur[i].FR,cur[i].SE));
cur=query(bel[a[v].p][2]);
for(int i=0;i<cur.size();i++) vt2.push_back(Ope('c',cur[i].FR+3,cur[i].SE+3));
cur=query(bel[a[v].p][3]);
for(int i=0;i<cur.size();i++) vt2.push_back(Ope('c',cur[i].FR+6,cur[i].SE+6));
memset(vis,0,sizeof(vis));
for(int i=1;i<=9;i++)
if (!vis[i]) {
int x=i;
do {
vis[x]=1;
if (!vis[a[v].val[x]]) vt2.push_back(Ope('D',i,a[v].val[x]));
x=a[v].val[x];
} while (!vis[x]);
}
int s=vt1.size()+vt2.size()+(a[u].kind==1);
puts("Yes");
printf("%d\n",s);
if (a[u].kind==1) puts("F");
for(int i=0;i<vt1.size();i++) printf("%c %d %d\n",vt1[i].ch,vt1[i].x,vt1[i].y);
for(int i=vt2.size()-1;i>=0;i--) printf("%c %d %d\n",vt2[i].ch,vt2[i].x,vt2[i].y);
}
int main() {
freopen("intellectual.in","r",stdin);
freopen("intellectual.out","w",stdout);
pre();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=9;j++) scanf("%s",str[i][j]+1);
search(n);
solve(n);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
output(i,j);
return 0;
}