题目大意
有n个5* 5的01矩阵,你可以选择一个矩阵中的位置,使此位置和其上下左右总共5个数取反。求从给出的矩阵变换到全是1的矩阵需要多少步,如果结果大于六步,输出-1。
如果一个一个枚举的话肯定是超时的。
所以:
已知“一个点不可以点两次”
我们可以先枚举第一行的状态(O(2^5)),枚举完之后,如果第一行存在0,那么得出第二行的这个位置必须点,其它位置不可以点(因为这样就会把第一行的其它1变成0了),由此得到第二行的状态,接着又可以推出第三行必须点的地方…推到第五行,看看是否符合条件就可以了。(O(25))
#include<cstdio>
int a[7][7],k[7][7],js,ans,n;
char c;
void cl(int d){ //翻第i行第d个数
a[1][d]^=1;
a[1][d-1]^=1;
a[1][d+1]^=1;
a[2][d]^=1;
}
void work(){
int kkk=js; //保护我计数变量
for(int i=1;i<5;++i) //枚举
for(int j=1;j<=5;++j)
if(k[i][j]==0){ //如果它是0,翻下一行这个位置的数
k[i][j]^=1;
k[i+1][j]^=1;
k[i+1][j-1]^=1;
k[i+1][j+1]^=1;
k[i+2][j]^=1;
++kkk; //需要的步数+1
}
for(int i=1;i<=5;++i) //判断一下是否符合条件
if(k[5][i]==0)
return;
if(kkk<ans) ans=kkk; //记一下
}
void s(int d){ //枚举第一行第d个位置是否点
if(d>5){ //第一行处理完毕
for(int i=1;i<=5;++i) //找个替身,保护我a数组状态
for(int j=1;j<=5;++j)
k[i][j]=a[i][j];
work(); //推
return;
}
s(d+1); //不翻
cl(d); //翻的操作
++js; //计翻的次数
s(d+1); //翻
--js; //重置
cl(d);
}
int main(){
scanf("%d",&n);
while(n>0){ //矩阵数
ans=7;
for(int i=1;i<=5;++i) //读入
for(int j=1;j<=5;++j)
scanf("%1d",&a[i][j]); //1位
s(1); //枚举第一行第1位是否要翻。
if(ans>6) printf("-1\n"); //答案数>6,如题
else printf("%d\n",ans);
--n;
}
}