——Frosty_Jackal 于9.14
由于‘N’较小,考虑用二进制压缩来处理。
令一个长度为9 的 01 数字串 中,1表示该数位上没填数,0表示填了。为什么用1表示没填?方便用lowbit函数找到这个‘1’值,从而找到要去填那个数。
由题意 对一个坐标所在九宫格可以由它的行,列确定:grid(x,y)=(x/3)*3 + y/3
由于是多组数据,预处理就显得尤为重要:
- 由题意,优先找到哪个坐标能填合法数字最少,去填。于是处理出每个状态有多少个1:
for(int i=0;i<(1<<SIZE); ++ i )
for(int j=i;j;j-=lowbit(j))
{
cnt[i]++;
}
- 对于一个lowbit运算,我们取出的是1所在的位置,怎么通过位置找到实际对应的填数?
即:
for(int i=0;i<SIZE;++i) num[(1<<i)]=i;
为了方便回溯,填数的时候用异或操作!
inline void filp(int x,int y,int z)
{
h[x]^=(1<<z);
l[y]^=(1<<z);
grid[getgrid1(x,y)]^=(1<<z);
}
输入输出采用这种格式最好!
for(int i=0;i<SIZE;++i)
for(int j=0;j<SIZE;++j)
sta[i][j]=s[i*9+j];
在输入中可以记tot=‘.’的总数,
DFS的过程可以以tot作为每一层状态转移的标志。
代码:
int SIZE=9,c;
char s[MAXN];char sta[21][21];
bool vi[12][12];
int h[11],l[11],grid[11],cnt[(1<<11)],num[(1<<11)];
inline int lowbit(int x){ return x& ( -x );}
inline int getgrid1(int x,int y)
{
return (x/3)*3+y/3;
}
inline void filp(int x,int y,int z)
{
h[x]^=(1<<z);
l[y]^=(1<<z);
grid[getgrid1(x,y)]^=(1<<z);
}
bool dfs(int now)
{
if(now==0) return true;
int temp=10,x,y;
for(int i=0;i<SIZE;++i)
for(int j=0;j<SIZE;++j)
{
if(sta[i][j]!='.') continue;
int val=h[i] & l[j] & grid[getgrid1(i,j)];
if(!val) return 0;
if(cnt[val]<temp)
{
temp=cnt[val];
x=i,y=j;
}
}
int val=(h[x] & l[y] & grid[ getgrid1(x,y)]);
for(;val;val-=lowbit(val))
{
int je=lowbit(val);
int c=num[je];
sta[x][y]=c+'1';
filp(x,y,c);
if(dfs(now-1)) return 1;
filp(x,y,c);
sta[x][y]='.';
}
return 0;
}
int main()
{
for(int i=0;i<(1<<SIZE); ++ i )
for(int j=i;j;j-=lowbit(j))
{
cnt[i]++;
}
for(int i=0;i<SIZE;++i) num[(1<<i)]=i;
while(scanf("%s",s) && s[2]!='d')
{
int tot=0;
for(int i=0;i<SIZE;++i)
for(int j=0;j<SIZE;++j)
{
sta[i][j]=s[i*9+j];
}
for(int i=0;i<SIZE;++i) h[i]=l[i]=grid[i]=(1<<9)-1;
for(int i=0;i<SIZE;++i)
for(int j=0;j<SIZE;++j)
{
if(sta[i][j]!='.')
{
filp(i,j,sta[i][j]-'1');
}
else tot++;
}
dfs(tot);
for(int i=0;i<SIZE;++i)
for(int j=0;j<SIZE;++j)
{
s[i*9+j]=sta[i][j];
}
puts(s);
}
return 0;
}
/*
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
expected answer:
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936
*/
是一个深搜为主题的大模拟。十分考验代码技巧。
首先是消除操作,不能逢3就消除,这样对于5个同色的情况便不能全部消除,应当先全部标记后统一消除。
inline bool remove()
{
int flag=0;
for(int i=1;i<=5;++i)
for(int j=1;j<=7;++j)
{
if(i-1>=1 && i+1<=5 && map[i-1][j]==map[i][j] && map[i][j]==map[i+1][j] && map[i][j])
{
xiao[i-1][j]=xiao[i][j]=xiao[i+1][j]=1;flag=1;
}
if(j-1>=1 && j+1<=7 && map[i][j-1] == map[i][j] && map[i][j]==map[i][j+1] && map[i][j])
{
xiao[i][j-1]=xiao[i][j]=xiao[i][j+1]=1;flag=1;
}
}
if(!flag) return 0;
for(int i=1;i<=5;++i)
for(int j=1;j<=7;++j)
{
if(xiao[i][j])
{
xiao[i][j]=0;
map[i][j]=0;
}
}
return 1;
}
其次是下落操作。
inline void update()
{
for(int i=1;i<=5;++i)
{
int x=0;
for(int j=1;j<=7;++j)
{
if(!map[i][j]) x++;
else
{
if(!x) continue;
map[i][j-x]=map[i][j];
map[i][j]=0;
}
}
}
}
然后是复制操作,用于回溯。
for(int i=1;i<=5;++i)
{
for(int j=1;j<=7;++j)
last[x][i][j]=map[i][j];
}
}
由于last的第一维度是步数,于是进一步深搜时,步数+1,不会被覆盖掉。
检查是否全部消除
inline bool check()
{
for(int i=1;i<=5;++i)
if(map[i][1]) return 0;
return 1;
}
左右移动操作:
注意,若移动并掉落后,可能会同色,此时需要不断进行消除和掉落操作保证状态图合法。
void move(int i,int j,int x)
{
int tmp=map[i][j];
map[i][j]=map[i+x][j];
map[i+x][j]=tmp;
update();
while(remove()) update();
}
搜索中的剪枝:
- 对于一个方块,若它左边有方块则不再往左边搜索,因为当枚举完左边那个方块时,已经通过搜索证明了往左搜的情况是不合法的。
- 在(1)对向左边搜的限制下,当右边同色,不再向右搜。
代码:
int n,map[MAXN][MAXN];
int ans[MAXN][38];
int last[MAXN][MAXN][MAXN];
bool xiao[MAXN][MAXN];
inline void update()
{
for(int i=1;i<=5;++i)
{
int x=0;
for(int j=1;j<=7;++j)
{
if(!map[i][j]) x++;
else
{
if(!x) continue;
map[i][j-x]=map[i][j];
map[i][j]=0;
}
}
}
}
inline bool remove()
{
int flag=0;
for(int i=1;i<=5;++i)
for(int j=1;j<=7;++j)
{
if(i-1>=1 && i+1<=5 && map[i-1][j]==map[i][j] && map[i][j]==map[i+1][j] && map[i][j])
{
xiao[i-1][j]=xiao[i][j]=xiao[i+1][j]=1;flag=1;
}
if(j-1>=1 && j+1<=7 && map[i][j-1] == map[i][j] && map[i][j]==map[i][j+1] && map[i][j])
{
xiao[i][j-1]=xiao[i][j]=xiao[i][j+1]=1;flag=1;
}
}
if(!flag) return 0;
for(int i=1;i<=5;++i)
for(int j=1;j<=7;++j)
{
if(xiao[i][j])
{
xiao[i][j]=0;
map[i][j]=0;
}
}
return 1;
}
void move(int i,int j,int x)
{
int tmp=map[i][j];
map[i][j]=map[i+x][j];
map[i+x][j]=tmp;
update();
while(remove()) update();
}
inline void copy(int x)
{
for(int i=1;i<=5;++i)
{
for(int j=1;j<=7;++j)
last[x][i][j]=map[i][j];
}
}
inline bool check()
{
for(int i=1;i<=5;++i)
if(map[i][1]) return 0;
return 1;
}
void dfs(int x)
{
if(check())
{
for(int i=1;i<=n;++i)
{
if(i!=1) printf("\n");
for(int j=1;j<=3;++j)
printf("%d ",ans[i][j]);
}
exit(0);
}
if(x==n+1) return ;
copy(x);
for(int i=1;i<=5;++i)
for(int j=1;j<=7;++j)
{
if(!map[i][j]) continue;
if(map[i][j]!=map[i+1][j] && i+1<=5)
{
move(i,j,1);
ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=1;
dfs(x+1);
for(int i=1;i<=5;++i)
{
for(int j=1;j<=7;++j)
{
map[i][j]=last[x][i][j];
}
}
ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
}
if(map[i-1][j]==0 && i-1>=1)
{
move(i,j,-1);
ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=-1;
dfs(x+1);
for(int i=1;i<=5;++i)
{
for(int j=1;j<=7;++j)
{
map[i][j]=last[x][i][j];
}
}
ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=5;++i)
for(int j=1;j<=8;++j)
{
int x; scanf("%d",&x);
if(x==0) break;
map[i][j]=x;
}
memset(ans,-1,sizeof(ans));
dfs(1);
puts("-1");
return 0;
}
/*
3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0
expected answer:
2 1 1
3 1 1
3 0 1
*/