USTCOJ 1240 黑屋 非位运算版

USTCOJ 1240,黑屋:http://acm.ustc.edu.cn/ustcoj/problem.php?id=1240

该题采用暴力枚举的方式求出关灯所需的最少步数。其中press数组标记了了在每一次尝试关闭所有灯时需要按下的按钮。

程序将原有的2^(m*n)中可能的按按钮操作缩减为了2^m次方种按按钮操作。大大缩减了搜索空间。对于每一种按按钮操作,均调用turnOffLight函数尝试关灯,若关灯成功,返回true,不能将所有等关闭,则返回false。turnOffLight函数执行完后,可通过press数组读取本次关灯所需按下的按钮。

该解法的加速增强版请参考:http://blog.csdn.net/l03071344/article/details/8816508

/***********
*原作者:liujianhua
*修改注释:lance
***********/
#include <cstdio>
#include <cstring>
#define MAXLEN 100
#define MAXWID  15

//数组开大一些,省去边界判断
int puzzle[MAXLEN + 5][MAXWID + 5];
char press[MAXLEN + 5][MAXWID + 5];
int n, m;

bool turnOffLight()
{
    int c, r;
    for(r = 1; r < n; ++r)
    {
        for(c = 1; c < m + 1; ++c)
            press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2;
    }
    for(c = 1; c < m + 1; ++c)
    {
        if((press[n][c - 1] + press[n][c] + press[n][c + 1] + press[n - 1][c]) % 2 != puzzle[n][c])
            return false;
    }
    return true;
}

//将计数部分剥离为一个函数
int count_step()
{
    int r, c, sum = 0;
    for(r = 1; r <= n; r++)
    {
        for(c = 1; c <= m; ++c)
        {
            if(press[r][c] == 1)
                ++sum;
        }
    }
    return sum;
}

int enumate()
{
    int minsteps = -1;
    int c = 0;
    while (1)
    {
        //每次while循环guess函数总会执行
		//从press[1]全0开始,即从不管第一行任何灯开始
        if (turnOffLight())
        {
            int step = count_step();
            if (minsteps == -1 || minsteps > step)
                minsteps = step;
        }
        //对press[1]数组进行二进制加法,枚举所有可能的关灯方式
        press[1][1]++;
        c = 1;
        while (press[1][c] > 1)
        {
            press[1][c++] = 0;
            press[1][c]++;      //进位
        }
        //通过判断进位是否已经到c,来确定是否已经枚举完2^m种可能
        if (c == m + 1)
            break;
    }
    return minsteps;
}

main()
{
    freopen("1240.in", "r", stdin);
    freopen("1240.out", "w", stdout);
    while((scanf("%d %d", &n, &m)) != EOF)
    {
    	//需要对press数组初始化,清除上一轮对press数组的设置
        memset(&press[0][0], 0, (n + 1) * (m + 1)); 
        int i, j;
        for(i = 1; i <= n; ++i)
        {
            for(j = 1; j <= m; ++j)
            {
                scanf("%d", &puzzle[i][j]);
                if(puzzle[i][j] == 1)
                    puzzle[i][j] = 0;
                else if(puzzle[i][j] == 0)
                    puzzle[i][j] = 1;
            }
        }
        //枚举所有可能的开灯方式,返回可能的最小步数
        int sum = enumate();
        if (sum == -1)
            printf("no solution\n");
        else
            printf("%d\n", sum);
    }
    return 0;
}

你可能感兴趣的:(ustcoj,1240)