题目链接

https://ac.nowcoder.com/acm/problem/50920

解题思路

很重要的思想就是,
1.枚举对第0行的全部操作方案,从第1行开始的每一行,用当前行的开关去维护上一行灯的亮灭
2.判断是否能使全部灯变亮,只需要去遍历最后一行,若不存在灭的灯,成立;因为前面的4行我们已经用其下一行都维护好了,都变成了亮的了。
3.当对第0行的操作固定时,是否能使全部灯变亮也已经确定了,且若可行,最少总转换开关的次数也就确定了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;

int dir[5][2]={0,1,0,-1,1,0,-1,0,0,0};//包括自己的转换 
int ans,n,mp[10][10],tmp[10][10],step,f;
string str[10];

void turn(int i,int j)
{
    for(int t=0;t<5;t++)
    {
        int ii=i+dir[t][0],jj=j+dir[t][1];
        if(ii<0 || ii>=5 || jj<0 || jj>=5) continue;
        tmp[ii][jj]^=1;
    }
}

int main()
{
    cin>>n;
    while(n--)
    {
        for(int i=0;i<5;i++)
        cin>>str[i];
        for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)
        mp[i][j]=str[i][j]-'0';//字符串转换成int数组 

        ans=INF;

        for(int s=0;s<(1<<5);s++)//枚举对第0行开关的操作,1代表摁下开关,0代表不摁开关(与灯亮灭无关) 
        {
            for(int i=0;i<5;i++)
            for(int j=0;j<5;j++)
            tmp[i][j]=mp[i][j];//把初始开关状态赋值到临时数组中,对临时数组操作 

            step=0;f=1;
            for(int i=0;i<5;i++)//对第0行的每一列遍历 
            if(s>>i&1)//如果当前列我们的操作是1,即摁下开关 
            {
                step++;//记录操作次数 
                turn(0,i);//对自己及周围四个(若存在4个)开关进行转换 
            }
            for(int i=1;i<5;i++)
            for(int j=0;j<5;j++)//遍历接下来的四行五列,每个开关只受它上面灯的影响 
            if(tmp[i-1][j]==0)//如果上面的灯是灭的,我们为了让全部灯变亮,就需要摁下当前开关,使得其上面的灯变亮 
            {
                step++;//记录操作次数 
                turn(i,j);//把当前灯转换 
            }

            for(int i=0;i<5;i++)//遍历第4行,即最后一行灯 
            if(tmp[4][i]==0)//如果存在灯是灭的,说明最初我们对第0行灯的操作不合法,无法使全部灯点亮 
            {
                f=0;
                break;
            }
            if(f) ans=min(ans,step);//如果操作方案合法,统计最小操作数 
        }
        if(ans>6) cout<<-1<<endl;//如果最小操作数都大于6,输出-1 
        else cout<<ans<<endl;
    }
}