AcWing 95. 費解的開關

小程xy發表於2024-04-30

原題連結
你玩過“拉燈”遊戲嗎?

25 盞燈排成一個 5×5 的方形。

每一個燈都有一個開關,遊戲者可以改變它的狀態。

每一步,遊戲者可以改變某一個燈的狀態。

遊戲者改變一個燈的狀態會產生連鎖反應:和這個燈上下左右相鄰的燈也要相應地改變其狀態。

我們用數字 1 表示一盞開著的燈,用數字 0 表示關著的燈。

下面這種狀態

10111
01101
10111
10000
11011
在改變了最左上角的燈的狀態後將變成:

01111
11101
10111
10000
11011
再改變它正中間的燈後狀態將變成:

01111
11001
11001
10100
11011
給定一些遊戲的初始狀態,編寫程式判斷遊戲者是否可能在 6 步以內使所有的燈都變亮。

輸入格式

第一行輸入正整數 n,代表資料中共有 n 個待解決的遊戲初始狀態。

以下若干行資料分為 n 組,每組資料有 5 行,每行 5 個字元。

每組資料描述了一個遊戲的初始狀態。

各組資料間用一個空行分隔。

輸出格式

一共輸出 n 行資料,每行有一個小於等於 6 的整數,它表示對於輸入資料中對應的遊戲狀態最少需要幾步才能使所有燈變亮。

對於某一個遊戲初始狀態,若 6 步以內無法使所有燈變亮,則輸出 −1。

資料範圍

0<n≤500

輸入樣例:
3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111
輸出樣例:

3
2
-1
題解

列舉每個方形的第一行的所有可能, 這裡用二進位制表示, 當前位上是 1 表示改變當前位置和周圍的燈
每種可能第一行確定之後, 後面的 n - 1 行的都有確定的情況, 最後判斷最後一行是否都為 1, 都為 1 並且改變的 次數 小於等於 6 的話滿足條件


下圖模擬的是樣例輸入的第一個方陣

  • 當 op 等於 16 的時候是該題的解, (16)2 = 10000, 表示改變第一行的第一個燈
  • [2,5]行的操作要根據它上一行燈的狀態, 當 (i - 1, j) 的燈是滅的, 就改變(i, j)位置的燈和它(上下左右的燈)
  • 圖中圈紅的 (3,3)位置上面的(2,3)燈是滅的, 所有改變(3,3)的燈和它上下左右的燈, 另一個圈紅的同上~~
#include <bits/stdc++.h>
using namespace std;
const int N = 10, n = 5;
char g[N][N], back[N][N];
int go[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
void change(int x, int y)
{
    g[x][y] = (g[x][y] == '0' ? '1' : '0');
    for (int i = 0; i < 4; i ++)
    {
        int a = x + go[i][0], b = y + go[i][1];
        if (a < 0 || a >= n || b < 0 || b >= n) continue;
        g[a][b] = (g[a][b] == '0' ? '1' : '0');
    }
}

int main()
{
    int T;
    cin >> T;
    while (T --)
    {
        for (int i = 0; i < n; i ++)
            cin >> g[i];
        
        vector<int> res;  // 存答案
        memcpy(back, g, sizeof g);  // 我們列舉的時候會改變 g 的狀態, back留作備份
        for (int op = 0; op < 32; op ++)  // [0,31] 的二進位制剛好列舉完第一行的所有可能 (從二進位制 [00000,11111])
        {  
            int step = 0;     // 記錄每種列舉的操作步數
            memcpy(g, back, sizeof back);  // 初始化 g 的狀態為 原狀態
            for (int j = 0; j < n; j ++)
                if (op >> j & 1)
                {
                    step ++;
                    change(0, n - j - 1);
                }

            for (int i = 1; i < n; i ++)
                for (int j = 0; j < n; j ++)
                    if (g[i - 1][j] == '0')
                    {
                        change(i, j);
                        step ++;
                    }

            bool f = true;
            for (int i = 0; i < n; i ++)
                if (g[n - 1][i] == '0')
                {
                    f = false;  // 最後一行還有滅的燈的話, 不滿足條件
                    break;
                }
            
            if (f && step <= 6) res.push_back(step);
        }
        if (res.size() == 0) cout << -1 << endl;
        else
        {
            sort(res.begin(), res.end());  // 步數最少, 取最小值
            cout << res[0] << endl;
        }
    }
    return 0;
}

覺得寫的不錯的話, 點個贊吧~

相關文章