POJ-2965 The Pilots Brothers' refrigerator (枚举)
题目传送门
题意
给定一个\(4\times 4\)的棋盘,每个格子有开启和关闭两种状态,每次选择一个格子,同时改变当前格子所处的行和列的所有状态,问需要几步才能将所有格子的状态变成开启,并输出每次选择的格子。
思路
本题与POJ-1753Flip Game 枚举类似,由于只有16个格子,我们可以用二进制状态存下当前的状态,通过对二进制不同位上的值来反应对映格子的状态,值得注意的是,如果不使用状态压缩的做法,可能会出现超时的情况。
参考代码
点此展开
//Author:Daneii
#include<iostream>
#include<queue>
using namespace std;
#define in(x) scanf("%d",&x)
#define lin(x) scanf("%lld",&x)
#define din(x) scanf("%lf",&x)
typedef long long ll;
typedef long double ld;
typedef pair<int,int> PII;
const int N=4;
int hh=0,tt=0;
struct node
{
int state;
int pos;
int step;
int pre;
}qs[1<<16+1];
bool used[1<<16+1];
void show(node cur)
{
if(cur.pre==-1)
return ;
show(qs[cur.pre]);
cout<<cur.pos/4+1<<' '<<cur.pos%4+1<<endl;//输出选取的点
}
int change(int s,int pos)
{
int cur=pos/4;//确定在哪一行
cur*=4;
for(int i=cur;i<cur+4;i++)//更新行
{
s^=(1<<i);
}
cur=pos%4;
for(int i=0;i<16;i+=4)//更新列
{
s^=(1<<(i+cur));
}
s^=(1<<pos);//翻转自己本身那个点
return s;
}
int main()
{
#ifdef LOCAL
freopen("D:/VSCodeField/C_Practice/.input/a.in", "r", stdin);
freopen("D:/VSCodeField/C_Practice/.input/a.out", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
node st;
st.step=0;
st.pre=-1;
st.state=0;
for(int i=0;i<16;i++)
{
char c;
cin>>c;
if(c=='+')
st.state|=(1<<i);
}
qs[tt++]=st;
used[st.state]=true;
while(hh<tt)
{
node tmp=qs[hh];
if(tmp.state==0)
{
cout<<tmp.step<<endl;
show(tmp);
return 0;
}
for(int i=0;i<16;i++)
{
node ns;
ns.state=change(tmp.state,i);
if(!used[ns.state])
{
ns.pre=hh;//记录前驱
ns.pos=i;//记录当前在哪里修改的
ns.step=tmp.step+1;//记录步数
qs[tt++]=ns;
used[ns.state]=true;
}
}
hh++;
}
return 0;
}