题目大意
给定
n
∗
m
n*m
n∗m 的起始状态和目标状态,状态中只含有R,B,G三个字符,给定两种操作,分别为:
1.选定一个
2
∗
2
2*2
2∗2 的子矩阵,将矩阵顺时针旋转
90
°
90°
90° 。
2.选定一个
2
∗
2
2*2
2∗2 的子矩阵,将矩阵的R变成B,B变成G,G变成R。
求起始状态最少经过多少次操作可以变为目标状态。
Input & Output
数据范围
2 ≤ n , m ≤ 4 2 \le n,m \le 4 2≤n,m≤4
思路
题目中格子个数最多为
16
16
16 个,而每一个格子可以放下
R
,
G
,
B
R,G,B
R,G,B 共
3
3
3 中颜色,所以状态总数为
3
16
=
43046721
3^{16}=43046721
316=43046721 ,可以用桶来记录状态。
所以我们就有有一个经典思路:由起始点出发
b
f
s
bfs
bfs 搜索,转移就为两种操作,看到达目标状态时的最小步数。
但是会
T
L
E
50
TLE 50
TLE50 。
再来观察一下题目,发现起始状态和目标状态都给定了,所以我们可以考虑双向广搜(DBFS) ,就是从起点和终点同时进行搜索(注意:从终点开始的搜索要逆向操作,即逆时针和颜色反着换),同时记录下每种状态到达的最小步数,如果起点和终点的状态重合,就可以输出答案了,时间复杂度快了一倍以上!
然后
T
L
E
90
TLE90
TLE90 。
我也很疑惑呢。。。
之后发现,转移时要找到
2
∗
2
2*2
2∗2 的矩阵的对应压缩后的数,如果暴力求解时间复杂度是
O
(
n
∗
m
)
O(n*m)
O(n∗m) 的,我们有一种
O
(
1
)
O(1)
O(1) 的求法:
假设我们要求的是坐标为
(
x
,
y
)
(x,y)
(x,y) 的颜色,原状态为
S
S
S ,如图:
按照压位的顺序,我们可以求出
(
x
,
y
)
(x,y)
(x,y) 所代表的位为
(
x
∗
m
+
y
)
(x*m+y)
(x∗m+y) (记为
l
l
l)。
那么我们将
(
x
,
y
)
(x,y)
(x,y) 移到最前面去,即
S
=
S
/
3
l
S=S/3^{l}
S=S/3l ,最后
S
m
o
d
3
S\mod 3
Smod3 即为所求的颜色啦。
每个格子都求一遍,只需求
4
4
4 遍,大大节约了时间复杂度,然后就可以愉快地AC啦ΦωΦ。
AC代码
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
int n,m,tot,st,en;
int a[10][10],f[1000800],p[19];
int d[10][10];
int num[10][10];
bool bz[43100400];
int to[43100400];
bool pd(int x,int y,int i,int j)
{
if(i>=x&&j>=y&&i-x<=1&&j-y<=1)
return 1;
return 0;
}
int updata(int x,int y)
{
return d[x][y]*num[x][y]+d[x+1][y]*num[x+1][y]+d[x][y+1]*num[x][y+1]+d[x+1][y+1]*num[x+1][y+1];
}
void find(int T,int x,int y)
{
if(T==0) d[x][y]=0,d[x][y+1]=0;
else if(T==1) d[x][y]=1,d[x][y+1]=0;
else if(T==2) d[x][y]=2,d[x][y+1]=0;
else if(T==3) d[x][y]=0,d[x][y+1]=1;
else if(T==4) d[x][y]=1,d[x][y+1]=1;
else if(T==5) d[x][y]=2,d[x][y+1]=1;
else if(T==6) d[x][y]=0,d[x][y+1]=2;
else if(T==7) d[x][y]=1,d[x][y+1]=2;
else if(T==8) d[x][y]=2,d[x][y+1]=2;
return;
}
int del(int S,int x,int y)
{
int l=(x-1)*m+(y-1);
int T=(S/p[l])%9;
find(T,x,y);
l=x*m+(y-1);
T=(S/p[l])%9;
find(T,x+1,y);
return updata(x,y);
}
void chan(int x,int y,int id)
{
if(d[x][y]==0)
if(id==1)
d[x][y]=1;
else
d[x][y]=2;
else if(d[x][y]==1)
if(id==1)
d[x][y]=2;
else
d[x][y]=0;
else if(d[x][y]==2)
if(id==1)
d[x][y]=0;
else
d[x][y]=1;
}
int change1(int S,int x,int y,int id)
{
int de=del(S,x,y);
int T=S-de;
if(id==1)
{
int t=d[x][y];
d[x][y]=d[x+1][y];
d[x+1][y]=d[x+1][y+1];
d[x+1][y+1]=d[x][y+1];
d[x][y+1]=t;
}
else
{
int t=d[x][y];
d[x][y]=d[x][y+1];
d[x][y+1]=d[x+1][y+1];
d[x+1][y+1]=d[x+1][y];
d[x+1][y]=t;
}
T+=updata(x,y);
return T;
}
int change2(int S,int x,int y,int id)
{
int de=del(S,x,y);
int T=S-de;
chan(x,y,id),chan(x+1,y,id),chan(x,y+1,id),chan(x+1,y+1,id);
T+=updata(x,y);
return T;
}
int bfs()
{
int h=0,t=1;
f[t]=st;
t++;
f[t]=en;
bz[st]=1;
bz[en]=0;
to[st]=0;
to[en]=0;
while(h!=t)
{
h=(h+1)%1000000;
int S=f[h];
for(register int i=1;i<n;i++)
for(register int j=1;j<m;j++)
{
int T=S;
T=change1(T,i,j,bz[S]);
if(to[S]!=-1&&to[T]!=-1&&bz[S]!=bz[T])
return to[S]+to[T]+1;
if(to[T]==-1)
{
bz[T]=bz[S];
t=(t+1)%1000000;
f[t]=T;
to[T]=to[S]+1;
}
T=change2(S,i,j,bz[S]);
if(to[S]!=-1&&to[T]!=-1&&bz[S]!=bz[T])
return to[S]+to[T]+1;
if(to[T]==-1)
{
bz[T]=bz[S];
t=(t+1)%1000000;
f[t]=T;
to[T]=to[S]+1;
}
}
}
return -1;
}
int main()
{
p[0]=1;
for(int i=1;i<=16;i++)
p[i]=p[i-1]*3;
while(1)
{
scanf("%d",&n);
if(n==0)
return 0;
memset(bz,0,sizeof(bz));
memset(to,-1,sizeof(to));
st=0,en=0;
scanf("%d",&m);
int lst=0;
num[1][1]=1,lst=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!(i==1&&j==1))
num[i][j]=lst*3,lst=num[i][j];
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
{
char ch=getchar();
while(ch!='R'&&ch!='B'&&ch!='G')
ch=getchar();
if(ch=='R') st=st+num[i][j]*0;
if(ch=='B') st=st+num[i][j]*1;
if(ch=='G') st=st+num[i][j]*2;
}
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
{
char ch=getchar();
while(ch!='R'&&ch!='B'&&ch!='G')
ch=getchar();
if(ch=='R') en=en+num[i][j]*0;
if(ch=='B') en=en+num[i][j]*1;
if(ch=='G') en=en+num[i][j]*2;
}
printf("%d\n",bfs());
}
}