CH0201费解的开关

思路

本题可以直接用bfs去做,时间复杂度较高,本文来说说递推的做法。

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 5;
const int INF = 10000000;
const int dx[5] = {0,1,0,-1,0};
const int dy[5] = {0,0,1,0,-1};


int t;
char g[N][N];

void turn(int x,int y)
{
    for(int i = 0;i < 5;i ++ )
    {
        int a = x + dx[i];
        int b = y + dy[i];
        if(a < 5 && a >= 0 && b < 5 && b >= 0)
        {
            g[a][b] ^= 1;
        }
    }
}

int work()
{
    int ans = INF;
    for(int i = 0;i < 1 << 5;i ++ )
    {
        int res = 0;
        char backup[N][N];
        memcpy(backup,g,sizeof g);
        for(int j = 0;j < 5;j ++ )
        {
            if(i >> j & 1)
            {
                res ++ ;
                turn(0,j);
            }
        }

        for(int i = 0;i < 4;i ++ )
        {
            for(int j = 0;j < 5;j ++ )
            {
                if(g[i][j] == '0')
                {
                    res ++ ;
                    turn(i + 1,j);
                }
            }
        }

        int flag = true;
        for(int i = 0;i < 5;i ++ )
        {
            if(g[4][i] == '0')
            {
                flag = false;
                break;
            }
        }

        if(flag) ans = min(ans,res);

        memcpy(g,backup,sizeof g);
    }
    if(ans > 6) ans = -1;
    return ans;
}

int main()
{
    cin >> t;
    while(t -- )
    {
        for(int i = 0;i < 5;i ++ ) cin >> g[i];

        cout << work() << endl;
    }
    return 0;
}

三个性质:

  1. 每个位置至多会被点击一次。
  2. 若固定了第一行,则满足题意的点击方案至多只有一种。其原因是:当第\(i\)行某一位为\(1\)时,若前\(i\)行已被固定,则只能点击\(i + 1\)行使第\(i\)行改变。
  3. 点击的先后顺序不影响结果。
    我们去枚举第一行的所有可能情况,并不改变第一行,只按第二行的开关。由性质\(2\)可得,若到达第\(n\)行不全为\(0\),说明第一行的这种枚举情况是错误的。若到达第\(n\)行全为\(0\),则说明第一行的枚举情况是正确的,更新一下答案即可。
posted @ 2021-08-05 10:58  面包络合物  阅读(112)  评论(1编辑  收藏  举报