思路:首先用枚举第一行的按开关情况,一共有32种情况(00000-11111),分别计算出这些情况按开关后亮灭情况
再通过改变第二行的将第一行的灯全亮,以此类推通过改变第三行使第二行全亮,改变第四行使第三行全亮,改变第五行使第四行全亮。
最后更新最小按灯数。
由于只有25个灯,int的有效范围是32位,故我们可以用int中的25位表示灯的情况。
为了表示方便我们将第一行第一个用0位表示,如下所示为五行灯对应int中的位数
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
为了满足上述对应,我们应该从后往前逐步更新便可以得到最开始的灯的情况g
for(int i=4;i>=0;i--) { for(int j=4;j>=0;j--) { if(a[i][j]=='1') g=(g<<1)+1; else g=g<<1; } }
接着我们枚举第一行的按灯情况00000-11111
对于安灯情况的变化,我们采用change函数完成
void change(int x,int& y)
{
y^=(1<<x);
if(x%5!=4) y^=(1<<(x+1));
if(x%5!=0) y^=(1<<(x-1));
if(x>4) y^=(1<<(x-5));
if(x<20) y^=(1<<(x+5));
}
由上面每个灯对应数字中的位数可以知道,当位数%5!=4时即不是最后一位,其右相邻的一位会转变,
当x%5!=0即不是每行首位时,其左相邻一位转变,当不是第一行时候,其前一行对应位置转变,不是最后一行,
其后一行位置转变。
进行完第一行枚举的转变后,从第一行开始扫描,如果发现没有亮的灯,利用第二行的转变使第一行的该位置灯变亮,以此类推,当前4行全亮后,判断第五行是不是全亮,是的话可以进行一次跟新。
#include<bits/stdc++.h> using namespace std; int n; string a[10]; int minz=1e9; void change(int x,int& y)//安灯 { y^=(1<<x); if(x%5!=4) y^=(1<<(x+1)); if(x%5!=0) y^=(1<<(x-1)); if(x>4) y^=(1<<(x-5)); if(x<20) y^=(1<<(x+5)); } void solve(int ch,int staus) { int cnt=0; for(int i=0;i<5;i++) { if((ch>>i)&1) { cnt++; change(i,staus); } } for(int i=0;i<4;i++) { for(int j=0;j<5;j++) { int v=i*5+j;//计算出该灯对应的位置 if(((staus>>v)&1)==0)//发现灭的灯 { cnt++; change((i+1)*5+j,staus);//因为利用下一行对应位置使该位置转变,故+5 } } } if((staus>>20)==31) minz=min(minz,cnt); } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin>>n; while(n--) { minz=1e9; for(int i=0;i<5;i++) { cin>>a[i]; } int g=0; for(int i=4;i>=0;i--) { for(int j=4;j>=0;j--) { if(a[i][j]=='1') g=(g<<1)+1; else g=g<<1; } } for(int i=0;i<32;i++) { solve(i,g); } if(minz>6) cout<<"-1"<<endl; else cout<<minz<<endl; } }