题目
Description
俄罗斯方块是一个四连通的,由
的小正方形构成的方块,并且可以被
的正方形装下,比如:
... @@@ @@@ .@@
... @@@ @.@ @.@
.@. @@@ @.. @@@
而这些就是不合法的:
@@. @.@ .@@.
... .@. @@@@
.@@ @.@ .@@.
其中@
表示方块,.
表示空格。
如果两个方块经过旋转,翻转,平移能变得一样,就认为它们本质相同。
如果你愿意去打个表,可以发现本质不同的方块只有
种。
这个游戏的目的,就是给一个
的棋盘,用某一种俄罗斯方块填成目标状态。
比如,这是一个目标状态对于两种俄罗斯方块的不同填法:
....11.. ....11..
...221.. ...221..
...211.. ...321..
...22... ...32...
.333.... .433....
4343.... 5444....
444..... 555.....
........ ........
对于任意两种不同的俄罗斯方块,你需要求出是否一个 的非空目标状态,使得它能被两种方块分别填充。
Input
第一行一个整数
表示数据组数。
接下来
组数据,每组数据由三行七列的字符串构成,第四列是空格,前三列和后三列表示一种方块。
Output
对于每组数据,如果存在一种构造,输出POSSIBLE
并给出一组构造,否则输出IMPOSSIBLE
。
你的构造可以使用数字,大小写字母,用不同的字符表示不同的方块,可以证明如果有解,则存在一种构造不会使用超过
种不同的方块。
Sample Input
2
.@@ .@.
.@. .@.
.@@ @@.
@@@ @@@
@.@ @@@
@@@ @@@
Sample Output
POSSIBLE
....11.. ....11..
...221.. ...221..
...211.. ...321..
...22... ...32...
.333.... .433....
4343.... 5444....
444..... 555.....
........ ........
IMPOSSIBLE
Constraint
本题代码长度限制
。
如果你回答对了
组数据,你的得分为
。
输出数据需要严格按照输出格式,不然可能会直接记为
分。如果对于一组数据你无法判断是否有解或是无法构造解,建议输出IMPOSSIBLE
来保证获得其他测试点的分。
分析
破题
我说的就是破题!不是破题。
题解:
(我的)代码:
分三步:
- 打出拼图块的表;
- 拼图块两两配对,状压搜索找答案;
- 运行10分钟左右出解,然后粘进程序,提交。
思(dai)路(ma)
打拼图块
考虑用9位二进制数压缩状态,直接枚举,判重。
判重时参考标程,利用GetWays
函数找到这个拼图块的所有可能情况(旋转,平移,对称等)并排序,这样构成了这个拼图块的一个等价类。对于每个等价类,它的标识就是状态压缩过后最小的那个(所以要排序+去重),这样方便判重。
注意Move
函数(将拼图块对齐到左上角)的神仙写法(我之前是模拟+暴力,看了标程才知道自己是憨憨),wxhtxdy!Rotate
和Reflect
函数模拟即可。当然还有判断4连通,dfs
暴力即可。
打拼图
对于拼图块i
和j
(二进制状压),跑dfs
,这里又膜一下大佬的神仙写法:
- 地图是
个格子,刚好用
usigned long long
状压; - 一个状态被表示为一个五元组
dfs(Board,Board0,Board1,x,y)
,其中前三个是usigned long long
(状压),后两个是坐标。Board
:可以理解为Board0|Board1
,决定了当前状态下是放i
还是放j
(例如Board
上某一格为1
,而Board0
的对应格不是,那肯定要放i
);Board0
,Board1
:分别表示i
拼出的形状和j
拼出的形状;x
,y
:当前坐标。
- 我们定义:把拼图块
i
放到地图的(x,y)
处,是指,让i'
的最下端的最右端与格子(x,y)
重合(i'
是i
中为1
的部分,当然,要先用Move
函数对齐),这就是Put
函数。例如:
因此这样放是不正确的(不能拿i
去跟(x,y)
配对):
我之前就是用第二种方法写,这样会漏情况!!!
- 找到了一样的就用
Save
函数存下来,返回即可。
细节
注意开始打之前要把格式弄好!!!不然打出来用不了又要重新打!!!!
打的时候可以时不时地打开table.txt
!!!!看看有没有问题!!!!
不要在main()
外面赋值,会编译超时(其实是根本编译不出来)!!!
C++11真好用!!!
代码
表就不展示了,,,,,,,
#include<bits/stdc++.h>
using namespace std;
#define NO {flag=0;break;}
#define ULL unsigned long long
vector<int> Blocks;
bool vis[(1<<9)-1];
const int Bit[5][5]={{0},{0,1<<8,1<<7,1<<6},{0,1<<5,1<<4,1<<3},{0,1<<2,1<<1,1<<0}};
const string Alphabet="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$";
int Move(int S){//wxhtxdy !!!!!!!!!! 不说了, 真的巧妙
while(!(S&448)) S<<=3;
//448二进制: 111 000 000
while(!(S&292)) S<<=1;
//292二进制: 100 100 100
return S;
}
int Now;
void dfs(int i,int j){
if(i<0||j<0||i>3||j>3||!(Now&Bit[i][j]))
return;
Now^=Bit[i][j];
dfs(i+1,j),dfs(i,j+1),
dfs(i-1,j),dfs(i,j-1);
}
bool isConnected(int S){
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(S&Bit[i][j]){
Now=S;
dfs(i,j);
return !Now;
}
return 0;
}
int Rotate(int S){
int ret=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(S&Bit[i][j])
ret|=Bit[j][4-i];
return ret;
}
int Reflect(int S){
int ret=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(S&Bit[i][j])
ret|=Bit[4-i][j];
return ret;
}
void Print(int S){
for(int i=1;i<=3;i++,puts(""))
for(int j=1;j<=3;j++)
putchar((S&Bit[i][j])?'@':'.');
}
vector<int> GetWays(int S){
vector<int> ret;
for(int t1=1;t1<=2;t1++,S=Reflect(S))
for(int t2=1,i=Move(S);t2<=4;t2++,i=Rotate(i)){
int j=i;
while(1){
int k=j;
while(1){
ret.push_back(k);
if(k&73) break;
//73二进制: 001 001 001
k>>=1;
}
if(j&7) break;
//7二进制: 000 000 001
j>>=3;
}
}
sort(ret.begin(),ret.end());
ret.erase(unique(ret.begin(),ret.end()),ret.end());
return ret;
}
void GetBlocks(){
int Full=(1<<9)-1;
for(int i=0;i<=Full;i++){
if(!isConnected(i))
continue;
vector<int> tmp=GetWays(i);
if(!vis[tmp[0]])//tmp[0]作为此方案的标识
vis[tmp[0]]=1,Blocks.push_back(i);
}
}
#define Pos(i,j) (1ull<<(((7-(i))<<3)+(8-(j))))
int Sign0,Sign1;
vector<int> Ways0,Ways1;
vector<string> Res0,Res1;
set<tuple<ULL,ULL,ULL,int,int> > Ban;
ULL Put(int x,int y,int S){
S=Move(S);
int Low=8;
while(!(S&(1<<Low)))
Low--;
x-=(8-Low)/3,y-=(8-Low)%3;//找到最下方的最右端, 转换参考系
ULL ret=0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(S&Bit[i][j]){
if(x+i-1<0||y+j-1<0||x+i-1>=8||y+j-1>=8)
return 0;
ret|=Pos(x+i-1,y+j-1);
}
return ret;
}
void Save(int id,int x,int y,int S){
S=Move(S);
int Low=8;
while(!(S&(1<<Low)))
Low--;
x-=(8-Low)/3,y-=(8-Low)%3;
int &P=id?Sign1:Sign0;
vector<string> &T=id?Res1:Res0;
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
if(S&Bit[i][j])
T[x+i-1][y+j-1]=Alphabet[P];
++P;
};
bool dfs(ULL Board,ULL Board0,ULL Board1,int x,int y){
if(Ban.count(make_tuple(Board,Board0,Board1,x,y)))
return 0;
if(Board0&&Board0==Board1)
return 1;
Ban.emplace(Board,Board0,Board1,x,y);
if(x>7)
return 0;
if(y>7)
return dfs(Board,Board0,Board1,x+1,0);
if(!(Board&Pos(x,y)))
return dfs(Board|Pos(x,y),Board0,Board1,x,y)||dfs(Board,Board0,Board1,x,y+1);
if(!(Board0&Pos(x,y))){
for(auto i:Ways0){
ULL tmp=Put(x,y,i);
if(!tmp||(Board0&tmp))
continue;
if(dfs(Board|tmp,Board0|tmp,Board1,x,y)){
Save(0,x,y,i);
return 1;
}
}
return 0;
}
if(!(Board1&Pos(x,y))){
for(auto i:Ways1){
ULL tmp=Put(x,y,i);
if(!tmp||(Board1&tmp))
continue;
if(dfs(Board|tmp,Board0,Board1|tmp,x,y)){
Save(1,x,y,i);
return 1;
}
}
return 0;
}
return dfs(Board,Board0,Board1,x,y+1);
}
void Solve(int x,int y){
puts("/*");
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++)
putchar(x&Bit[i][j]?'@':'.');
putchar(' ');
for(int j=1;j<=3;j++)
putchar(y&Bit[i][j]?'@':'.');
putchar('\n');
}
puts("*/");
Ban.clear();
Sign0=0,Sign1=0;
Ways0=GetWays(x),Ways1=GetWays(y);
Res0.clear(),Res0.resize(8,"........");
Res1.clear(),Res1.resize(8,"........");
if(dfs(0,0,0,0,0)){
printf("Ans[%d][%d]=",x,y);
printf("\"POSSIBLE\\n\"\n");
for(int i=0;i<7;i++)
cout<<"\""<<Res0[i]<<" "<<Res1[i]<<"\\n\"\n";
cout<<"\""<<Res0[7]<<" "<<Res1[7]<<"\\n\";\n";
printf("Ans[%d][%d]=",y,x);
printf("\"POSSIBLE\\n\"\n");
for(int i=0;i<7;i++)
cout<<"\""<<Res1[i]<<" "<<Res0[i]<<"\\n\"\n";
cout<<"\""<<Res1[7]<<" "<<Res0[7]<<"\\n\";\n";
}
else{
printf("Ans[%d][%d]=",x,y),puts("\"IMPOSSIBLE\\n\";");
printf("Ans[%d][%d]=",y,x),puts("\"IMPOSSIBLE\\n\";");
}
puts("");
}
void GetTable(){
freopen("table.txt","w",stdout);
GetBlocks();
for(auto i:Blocks){
for(auto j:Blocks){
if(j<=i)
continue;
Solve(i,j);
}
}
}
int main(){
ios::sync_with_stdio(0);
vector<vector<string> > Ans(512,vector<string>(512));
/*此处省略一万行的表*/
/*调用GetTable(), 然后把table.txt里面的粘到这里即可*/
/*大约需要11分钟, 请耐心等待*/
int T;
scanf("%d",&T);
while(T--){
int x=0,y=0;
for(int i=1;i<=3;i++){
char str0[5],str1[5];
scanf("%s%s",str0+1,str1+1);
for(int j=1;j<=3;j++){
if(str0[j]=='@')
x|=Bit[i][j];
if(str1[j]=='@')
y|=Bit[i][j];
}
}
x=GetWays(x)[0],y=GetWays(y)[0];
cout<<Ans[x][y];
}
}