题解:P8634 [蓝桥杯 2015 国 A] 铺瓷砖
kkksc03wzl · · 题解
爆肝一个下午,终于铺好瓷砖了!!!!!!!!!!!!!!!!!
前言
当我打开这道题目,发现应该是个矩阵优化递推的板子,然后苦苦思索良久也一点思路没有。于是我打开题解,发现看不懂一点。于是第二天,教练讲了讲这道题,说是爆出来所有的状态,暴力连边。最后把有用的状态留下来做矩阵乘法就行了。于是就开启了我一下午的战斗。。
SOLUTION
上面已经说了一部分思路,我们发现因为最长的块跨越了三行,所以我们的状态至少要设两行的,首先考虑直接对于每个位置用
这样就是
然后我们考虑怎么爆搜状态之间的连边。我们先暴力得把所有状态都枚举一遍,从每一个状态出发,往里面铺瓷砖。我们不妨钦定从上到下,从左到右铺,把每一种瓷砖塞进去看看合不合法,直到这一行填完,并且把下面两行的状态记录下来,矩阵对应边加一。
接下来的问题就是,我们如何去把每一种瓷砖放进去,如何判断合法了。这里提供一个本人用的方法,不妨考虑把每一种瓷砖的旋转四个方向都存下来,每一个方向的瓷砖用三个数字表示,数字对应的二进制位就是对应位置是否有砖块。同样的,我们把当前
下面会出示关键代码:
//爆搜
int block[8][3]={{2,3,0},{1,3,0},{3,1,0},{3,2,0},{7,2,0},{2,7,0},{2,3,2},{1,3,1}};
int sta[3];
inline int get(){
int ans=0;
int mn=0;
for(int j=m-1; j>=0; j--){
int now=((sta[1]>>j)&1)+((sta[2]>>j)&1);
ans=ans*3+now-mn;
}
return ans;
}
void dfs(int x){
if(x==m+1)return ++jz[now][dy[get()]],void();
if(!((sta[0]>>(x-1))&1)){
for(int i=0; i<8; i++){
for(int j=x-3; j<=x-1; j++){
if(j<0)continue;
if((((block[i][0]<<j)|sta[0])>>(x-1))&1){
int st0=(block[i][0]<<j)&sta[0],st1=(block[i][1]<<j)&sta[1];
if(st0||st1){continue;}
sta[0]^=(block[i][0]<<j),sta[1]^=(block[i][1]<<j),sta[2]^=(block[i][2]<<j);
if((sta[0]<(1<<m))&&(sta[1]<(1<<m))&&(sta[2]<(1<<m))){
dfs(x+1);
}
sta[0]^=(block[i][0]<<j),sta[1]^=(block[i][1]<<j),sta[2]^=(block[i][2]<<j);
}
}
}
}else{
dfs(x+1);
}
}
代码上没加注释,马蜂也比较凌乱,只是放在这供参考,这道题一定要自己动手写一写。
“这是一道不可多得的好题,不,是可多得的好题,因为换几个形状就可以再出一道。”——某教练