USTCOJ1240 黑屋 位運算

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

該題採用暴力枚舉的方式,通過位運算加速開關燈操作。

若理解有困難,可移步“USTCOJ 1240 黑屋 非位運算版”http://blog.csdn.net/l03071344/article/details/8884790瞭解該解法基本思想。


百練相關題目爲:http://poj.grids.cn/practice/1753。相關解法有:

http://www.cnblogs.com/HappyAngel/archive/2010/05/12/1734108.html
http://blog.chinaunix.net/uid-22263887-id-1778972.html
http://www.cnblogs.com/shuaiwhu/archive/2012/04/27/2474041.html
http://www.cppblog.com/Yusi-Xiao/archive/2009/03/21/77383.html

/********************************************
*原作者:scuwf。代碼編號:53947
*代碼完善及註釋作者:baird。代碼編號:71756
*第一次代碼完善及註釋作者:baird。代碼編號:71756
*第二次代碼完善及註釋作者:Lance。代碼編號:71824
********************************************/

#include <stdio.h>
#define MAX 150             //最大數據規模
#define MAX_CASE 0x8000     //最大測試數量,2 ^ 15
//此三個數組爲全局變量,會自動初始化爲全0。多出的一行的爲了方便操作。
int lightmap[MAX+1], tempmap[MAX+1], switchstep[MAX_CASE];  
//i表示要變換的行的序號。該函數變換第i行中,k的二進制中標爲1的位。
//以及這些對應位左側的燈、右側的燈、下方的燈。
void run_switch(int i, int kk, int m)
{
    tempmap[i] ^= kk;           //改變對應位置燈泡狀態
    tempmap[i] ^= kk >> 1;      //改變對應位置右側燈泡狀態
    tempmap[i] ^= (kk << 1) & ((1 << m) - 1);//改變對應位置左側燈泡狀態
    tempmap[i + 1] = lightmap[i + 1] ^ kk;  //改變對應位置下方燈泡狀態
}

//生成每一種行變換的所需的操作步驟
void count_switchstep(void)
{
    int i, t;
    for (i = 0; i < MAX_CASE; i++) //i代表行變換的編號。這裏for循環記錄了所有的行變換所需的
    {
        switchstep[i] = 0;         //初始化,switchstep[i]存儲了i的二進制表示中1的個數
        t = i;
        while (t)
        {
            switchstep[i] += t & 1;
            t >>= 1;
        }
    }
}

int main(void)
{
    count_switchstep(); //生成每一種行變換的操作步驟
    int m, n, i, j, k, step, minstep;
    while (~scanf("%d%d", &n, &m))
    {
        minstep = -1; //定義每組測試數據的最小步驟並初始化爲下確界
        for (i = 0; i < n; i++)
        {
            lightmap[i] = 0;
            for (j = 0; j < m; j++)
            {
                scanf("%d", &k);
                lightmap[i] += k << (m - 1 - j); //讀入燈泡地圖,用二進制的形式儲存每一行的情況
            }
        }
        //暴力枚舉第一行的所有可能的變換,一共有2 ^ m次方種變換。
        //例如k=1000 0000 0001。表示變換第一個和最後一個燈。
        //因爲與1異或,相當於對改位取反;與0異或,該位不變。
        for (k = 0; k < 1 << m; k++) 
        {
            step = switchstep[k];       //對第一行進行該種變換所需步數
            tempmap[0] = lightmap[0];   //初始化第一行數據到testmap中
            run_switch(0, k, m);
            for (i = 1; i < n; i++)  //從第二行開始變換,每一行要保證上一行的燈全開(即全爲1)。
            {
                //~testmap[i - 1] & ((1 << m) - 1)是由上一行燈泡情況而確定的本行的變換,
                //目的是要保證上一行燈全開
                int kk = ~tempmap[i - 1] & ((1 << m) - 1);
                step += switchstep[kk];   //累積在指定第一行變換下,將全部燈打開所需的步數
                run_switch(i, kk, m);
            }
            //通過考察最後一行是否全1,來判斷本輪變換是否將所有燈都打開。
            //若條件成立,再且與當前最小步數進行比較
            //當minstep值爲-1時,step < (unsigned)minstep總是成立。
            if (tempmap[n - 1] == (1 << m) - 1 && step < (unsigned)minstep)
                minstep = step;         
        }
        //打印結果
        if (minstep == -1)
            printf("no solution\n");
        else
            printf("%d\n", minstep);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章