[递推] aw95. 费解的开关(二维递推+开关问题+二进制枚举)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[递推] aw95. 费解的开关(二维递推+开关问题+二进制枚举)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:95. 费解的开关

一维递推:[递推] aw3777. 砖块(思维+递推+CF1271B)

2. 题目解析

这个是 十字形 改变灯的状态的,并且只有 5*5 的棋盘大小,所以总的状态数量是 2 5 ∗ 5 2^{5*5} 255 暴力枚举肯定是不可行的。

两个性质:

  • 每个点只会操作一次。
  • 操作顺序是任意的。

那么,当我们操作完第一行之后,第一行已经全为 1,那么第二行的 0,只能通过操作第三行的同列元素进行改变,直到操作到第 4 行,将第 4 行变为全 1,当此时第 5 行也变为全 1 的话,则到达最终状态,是一个可行方案,更新答案即可。

在此需要注意,只是更新答案,并不是最优解。因为第一行的操作是任意的,且只有确定第一行才能递推后面的所有行,且第一行中的任意一种操作都可能是最优解中需要的操作,所以需要枚举第一行的所有操作,是 2 5 2^5 25 个,然后不断更新最优解。


不过本题,既然限制了步数的层数,那么意味着迭代加深应该也是可以做的,不过并没看到这样的写法,待思考。


时间复杂度: O ( 2 5 ∗ 5 ∗ 5 ∗ 500 = 2 e 6 ) O(2^5*5*5*500=2e6) O(2555500=2e6),其中 500 是 500 个测试数据

空间复杂度: O ( n ) O(n) O(n)


#include <bits/stdc++.h>

using namespace std;

const int N = 6;

char g[N][N], backup[N][N];
int dx[5] = {-1, 0, 1, 0, 0}, dy[5] = {0, 1, 0, -1, 0};     // 上右下左+自己

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;
        if (g[a][b] == '0') g[a][b] = '1';
        else g[a][b] = '0';
    }
}

int main() {
    int T; cin >> T; while (T -- ) {
        for (int i = 0; i < 5; i ++ ) cin >> g[i];
        
        int res = 1e9;
        for (int op = 0; op < 1 << 5; op ++ ) { // 第一行的所有操作
            memcpy(backup, g, sizeof g);
            int step = 0;
            for (int i = 0; i < 5; i ++ )       // 操作第一行,记录个数
                if (op >> i & 1) {
                    step ++ ;
                    turn(0, i);                 // 改变0行i列的状态
                }
                
            for (int i = 0; i < 4; i ++ )       // 第i行决定i+1行的状态,递推即可
                for (int j = 0; j < 5; j ++ ) {
                    if (g[i][j] == '0') {
                        step ++ ;
                        turn(i + 1, j);         // 改变i+1行j列的状态
                    }
                }
                
            bool dark = false;                  // 判断最后一行是否仍有开关是灭的,为非法状态
            for (int i = 0; i < 5; i ++ ) {
                if (g[4][i] == '0') {
                    dark = true;
                    break;
                }
            }
            
            if (!dark) res = min(res, step);    // 如果合法,则为一个可行方案,答案步数取min即可
            
            memcpy(g, backup, sizeof g);
        }
        if (res > 6) res = -1;                  // 六步内无法恢复,则输出 -1
        cout << res << endl;
    }
    
    return 0;
}

以上是关于[递推] aw95. 费解的开关(二维递推+开关问题+二进制枚举)的主要内容,如果未能解决你的问题,请参考以下文章

AcWing95 费解的开关 (递推)

95. 费解的开关(Acwing)(分析+递推)

算法刷题AcWing 95. 费解的开关——递推

AcWing - 95 - 费解的开关(递推)

AcWing 95 费解的开关

题解AcWing95费解的开关