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