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;
}