题目链接:https://ac.nowcoder.com/acm/contest/998/D
题意:就是给出t组询问数据,然后每一组数据给出一个5*5的矩阵,矩阵元素是由0、1构成,并且每一次按动一个元素会导致其上下左右与自身共计5个位置的元素发生改变,即0<->1,闲现在请问你能否再6步之内将所有的0都变为1。
思路:我们通过观察,发现既然每按动一个按钮都会导致周围五个按钮状态发生变化,不难想到判断第i排是否为0,若为0我们就通过按第i+1排的按钮来改变第i排按钮的状态,并且通过这样的操作,将第i排的状态就永远固定为1了,(因为下一次是判断第i+1排,通过改变第i+2排,而第i+2排的改变并不会影响到第i排按钮状态),最后通过判断最后一排是否全部为1来判断方案是否可行。
技巧:
1.通过二进制枚举第一排的操作可能,还是与之前的二进制思想相同,判断位数是否为1来进行操作。因为通过枚举二进制数,是可以枚举完所有的可能性的,因为比如有n个按钮,那么其操作方案数就是2n种,而区间[0,1 << n)刚好就是包含了所有可能性。
2.每一次枚举第一排的操作方案,都要先将初始情况保存,再进行完操作之后,再恢复初始状态,运用memcpy(a,b,sizeof(b))可以快速完成这一操作。
3.每一次按动开关进行操作,都需要对步数递增,最终通过最小步数判断答案是否符合要求。
做题代码技巧:先布局,再写具体的实现函数。
代码:
#include <bits/stdc++.h>
using namespace std;
const int INF = 10000005;
char s[10][10];
int dix[5] = {0, -1, 1, 0, 0};
int diy[5] = {0, 0, 0, 1, -1};
void turn(int a, int b)
{
for(int i = 0; i < 5; i++)
{
int x = a + dix[i];
int y = b + diy[i];
if(x >= 0 && x < 5 && y >= 0 && y < 5) s[x][y] ^= 1;
}
}
int work()
{
int ans = INF;
for(int k = 0; k < 1 << 5; k++)
{
int res = 0;
char backup[10][10];
memcpy(backup, s, sizeof(s));
for(int i = 0; i < 5; i++)
if(k >> i & 1)
{
res ++;
turn(0, i);
}
for(int i = 0; i < 4; i++)
for(int j = 0; j < 5; j++)
if(s[i][j] == '0')
{
turn(i + 1, j);
res++;
}
bool is_ok = true;
for(int i = 0; i < 5; i++)
if(s[4][i] == '0')
{
is_ok = false;
break;
}
if(is_ok) ans = min(ans, res);
memcpy(s, backup, sizeof(s));
}
return ans > 6 ? -1 : ans;
}
int main()
{
ios::sync_with_stdio(false);
int t; cin >> t;
while(t--)
{
for(int i = 0; i < 5; i++) cin >> s[i];
cout << work() << endl;
}
return 0;
}