【Acwing】【Entrenamiento Copa Puente Azul】【Recursivo】

interruptor inexplicable

¿Alguna vez has jugado el juego de "tirar de las luces"?

25 luces están dispuestas en un cuadrado de 5 x 5.

Cada luz tiene un interruptor que el jugador puede cambiar su estado.

En cada paso, el jugador puede cambiar el estado de cierta luz.

Cuando el jugador cambia el estado de una luz, se producirá una reacción en cadena: las luces adyacentes a la luz, arriba, abajo, izquierda y derecha también cambiarán sus estados en consecuencia.

Usamos el número 1 para representar una luz encendida y el número 0 para representar una luz apagada.

el siguiente estado

10111
01101
10111
10000
11011

Después de cambiar el estado de la luz superior izquierda, se convertirá en:

01111
11101
10111
10000
11011

Después de cambiar la luz en el medio, el estado será:

01111
11001
11001
10100
11011

Dado un estado inicial del juego, escribe un programa para determinar si es posible que el jugador haga que todas las luces brillen en 6 movimientos.

formato de entrada

La primera línea ingresa un número entero positivo n, lo que significa que hay n estados iniciales del juego para resolver en los datos.

Las siguientes líneas de datos se dividen en n grupos, cada grupo de datos tiene 5 líneas y cada línea tiene 5 caracteres.

Cada conjunto de datos describe el estado inicial de un juego.

Cada conjunto de datos está separado por una línea en blanco.

formato de salida

Se genera un total de n líneas de datos, y cada línea tiene un número entero menor o igual a 6, lo que indica la cantidad mínima de pasos necesarios para que el estado del juego correspondiente en los datos de entrada haga que todas las luces brillen.

Para un determinado estado inicial del juego, si no se pueden encender todas las luces en 6 pasos, emite −1.

rango de datos

0 < norte ≤ 500

Muestra de entrada:

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

Salida de muestra:

3
2
-1

解题思路:

可以分析出每一行的操作是由该行上一行的灯的亮灭状态来决定的
若对应列的上一行的灯是灭的,对应列的下一行的灯一定要操作一下,形成一层一层递推的过程。

可以通过二进制枚举第一行的所有操作状态,0表示不操作,1表示操作,通过第一行确定了的状态
去递推下面的行,最后判断最后一行是否是全亮。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 6;
int n;
char g[N][N], backup[N][N];
int dx[5] = {
    
    -1, 0, 1, 0, 0}, dy[5] = {
    
    0, 1, 0, -1, 0};

// 这个操作是把(x, y)以及上下左右的灯都变成相反的状态
void turn(int x, int y)
{
    
    
    for (int i = 0; i < 5; i ++)
    {
    
    
        int a = x + dx[i], b = y + dy[i];
        if (a < 0 || a >= 5 || b < 0 || b >= 5) continue;   // 在边界外,忽略
        g[a][b] ^= 1; // 异或技巧
    }
}

int main()
{
    
    
    cin >> n;
    while (n --)
    {
    
    
        for (int i = 0; i < 5; i ++) cin >> g[i];
        
        int res = 10;
        // 第一行相当于是每次锁死的
        // 这里我们枚举了第一行的32种按法,不用管是亮是灭,把第一行所有情况都按一遍
        // 按每种情况的第一行,去遍历接下来的行
        // 枚举32种第一行的按法只是可能会减少步数,
        // 如果直接从第二行开始答案一定是固定的了,找不到最优解或者可能没有解
        for (int op = 0; op < 32; op ++) 
        {
    
    
            // 原始数组进行备份,操作完一次第一行的一种按法后要还原
            memcpy(backup, g, sizeof backup);
            int step = 0; // 步数
            
            // 具体出第一行的按法情况(通过位运算 取出二进制中的1在哪一位即可)
            for (int i = 0; i < 5; i ++)
                // 以op为2为例 对应的二进制表示是 00010
                // 00010 >> 1 & 1 = 1  只有当i = 1 时才会去执行turn(0, 1) 就是第二个位置按了一下,其它位置不按
                if (op >> i & 1) 
                {
    
    
                    turn(0, i);
                    step ++; // 操作次数 ++
                }
                
            // 然后通过第一行按完之后的状态(递推过程)
            for (int i = 0; i < 4; i ++)
                for (int j = 0; j < 5; j ++)
                    if (g[i][j] == '0') // 如果当前的灯是灭的
                    {
    
    
                        turn(i + 1, j); // 灭着的灯的下一行对应的位置就一定要按一下
                        step ++;
                    }
            // 至此,全部按完后只需判断最后一行的灯是不是全亮着即可判断所有灯是不是全亮
            bool dark = false;
            for (int j = 0; j < 5; j ++)
                if (g[4][j] == '0')
                {
    
    
                    dark = true;
                    break;
                }
            if (!dark) res = min(res, step); // 没有黑暗的灯,更新最小步数即可
            memcpy(g, backup, sizeof g); // 备份还原
        }
        if (res > 6) res = -1;
        cout << res << endl;
    }
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/laaa123mmm/article/details/129391204
Recomendado
Clasificación