费解的开关(状压)

题目描述

你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011

给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。

 

输入

第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n<=5;
对于100%的数据,n<=500。

 

输出

输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。 

 

样例输入

3
00111
01011
10001
11010
11100

11101
11101
11110
11111
11111

01111
11111
11111
11111
11111

 

样例输出

3
2
-1 

1.每个位置至多只会被点击一次
2.若固定了第一行,则满足题意的点击方案至多只有一种。因为当第i行某一位为1时,若前i行已经被固定,只能点击i+1行该位置上的数字才能使第i行的这一位置变成0。

在图确定的情况下,确定第i行的点击方案,第i+1行的点击方案也随之确定。所以状压枚举第一行所有点击方案,共2⁵ 32种;

一、用0x3f3f3f3f表示maxn;
二、用二进制的01表示状态;

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5;
const int inf = 0x3f3f3f3f;

char _map[maxn][maxn + 5];
char sta[maxn][maxn + 5];
int times, min_times;
int dx[5] = {-1, 0, 0, 1};
int dy[5] = {0, 1, -1, 0};

bool check(int x, int y) {
    if (x >= 0 && x < 5 && y >= 0 && y < 5) return 1;
    else return 0;
}

void press(int x, int y) {
    sta[x][y] = (sta[x][y] == '0') ? '1' : '0';
    for (int i = 0; i < 4; i++) {
        if (check(x + dx[i], y + dy[i]))
            sta[x + dx[i]][y + dy[i]] = (sta[x + dx[i]][y + dy[i]] == '0') ? '1' : '0';
    }
    times++;
}

int main() {
    int n;
    cin >> n;
    while (n--) {
         min_times = inf;
        for (int i = 0; i < maxn; i++)
            cin >> _map[i];
        for (int i = 0; i < (1 << maxn); i++) {
            times=0;
            memcpy(sta, _map, sizeof(_map));
            int now = i;
            for (int j = 0; now; j++, now >>= 1) {
                if (now & 1)
                    press(0, j);
            }
            for (int j = 1; j < maxn; ++j) {
                for (int k = 0; k < maxn; ++k) {
                    if (sta[j - 1][k] == '0')
                        press(j, k);
                }
            }
            int flag = 1;
            for (int j = 0; j < maxn; j++) {
                for (int k = 0; k < maxn; ++k) {
                    if (sta[j][k] == '0')
                        flag = 0;
                }
            }
            if (flag) min_times = min(times, min_times);
        }
        if (min_times > 6) cout << -1 << endl;
        else cout << min_times << endl;
        //cout<<min_times<<endl;
    }
    return 0;
}

 

posted @ 2018-07-08 16:58  Albert_liu  阅读(168)  评论(0编辑  收藏  举报