题意
游戏“The Pilots Brothers:跟随有条纹的大象”有一个玩家需要打开冰箱的任务。
冰箱门上有16个把手。每个手柄可以处于以下两种状态之一:打开或关闭。只有当所有把手都打开时,冰箱才会打开。手柄表示为矩阵4х4。您可以在任何位置[i,j](1≤i,j≤4)更改句柄的状态。但是,这也会更改第i行中所有句柄的状态以及第j列中的所有句柄。
任务是确定打开冰箱所需的最小手柄切换次数。
思路
一个和“费解的开关”,"棋盘翻转",这样的位运算的题目很像,只不过这次一次翻转1行+1列
其实可以用1个数就可以记录状态,但是我懒……
(其实是不会状压啦)
Code
#include<cstdio> using namespace std; #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) int x[20], y[20]; int map[6][6]; int ans=33; int ansX[20],ansY[20]; int read() { char ch = getchar(); while (ch!='-' && ch!='+') ch = getchar(); return ch=='-'?1:0; } void build() { for(int i=0;i<4;i++) for (int j = 0; j < 4; j++) map[i][j] = read(); } void flip(int s) { int x1 = s / 4; int y1 = s % 4; for (int i = 0; i < 4; i++) { map[i][y1] ^= 1; map[x1][i] ^= 1; } map[x1][y1] ^= 1; } bool check() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) if (!map[i][j]) return 0; } return 1; } void dfs(int s, int b) { if (check()) { if (ans > b) { ans = b; for (int i = 1; i <= ans; i++) ansX[i] = x[i], ansY[i] = y[i]; } return; } if (s >= 16) return; dfs(s + 1, b); flip(s); x[b + 1] = s / 4 + 1; y[b + 1] = s % 4 + 1; dfs(s + 1, b + 1); flip(s); return; } int main() { build(); dfs(0, 0); printf("%d ", ans); for (int i = 1; i <= ans; i++) printf("%d %d ", ansX[i], ansY[i]); return 0; }
其他做法
博客上搜到的
放出来
注释也很详细了
用奇偶性做的
/* 参考高手的高效解法: > 证明:要使一个为'+'的符号变为'-',必须其相应的行和列的操作数为奇数;可以证明,如果'+'位置对应的行和列上每一个位置都进行一次操作,则整个图只有这一'+'位置的符号改变,其余都不会改变. > 设置一个4*4的整型数组,初值为零,用于记录每个点的操作数,那么在每个'+'上的行和列的的位置都加1,得到结果模2(因为一个点进行偶数次操作的效果和没进行操作一样,这就是楼上说的取反的原理),然后计算整型数组中一的 > 个数即为操作数,一的位置为要操作的位置(其他原来操作数为偶数的因为操作并不发生效果,因此不进行操作) ********************************* 此上证其可以按以上步骤使数组中值都为‘-’ ******************************** 在上述证明中将所有的行和列的位置都加1后,在将其模2之前,对给定的数组状态,将所有的位置操作其所存的操作数个次数,举例,如果a[i][j]==n,则对(i,j)操作n次,当所有的操作完后,即全为‘-’的数组。 其实就是不模2的操作,作了许多的无用功。 以上的操作次序对结果无影响,如果存在一个最小的步骤,则此步骤一定在以上操作之中。(简单说下:因为以上操作已经包含了所有可改变欲改变位置的操作了) 而模2后的操作是去掉了所有无用功之后的操作,此操作同样包含最小步骤。 但模2后的操作去掉任何一个或几个步骤后,都不可能再得到全为‘-’的。(此同样可证明:因为操作次序无影响,先进行最小步骤,得到全为‘-’,如果还剩下m步,则在全为‘-’的数组状态下进行这m步操作后还得到一个全为 ‘-’的数组状态,此只能是在同一个位置进行偶数次操作,与前文模2后矛盾,所以m=0),因此模2后的操作即为最小步骤的操作。 */ #include <iostream> using namespace std; bool mark[4][4]; char s[4][4]; int main() { int i,j,k; int ci[16],cj[16]; int nas = 0; memset(mark,0,sizeof(mark)); for(i = 0;i < 4;i++) cin >> s[i]; for(i = 0;i < 4;i++) for(j = 0;j < 4;j++) { char c = s[i][j]; if(c == '+') { mark[i][j] = !mark[i][j]; for(k = 0;k < 4;k++) { mark[i][k] = !mark[i][k]; mark[k][j] = !mark[k][j]; } } } for(i = 0;i < 4;i++) for(j = 0;j < 4;j++) if(mark[i][j] == true) { ci[nas] = i + 1; cj[nas] = j + 1; nas ++; } printf("%d ",nas); for(i = 0;i < nas;i++) { printf("%d %d ",ci[i],cj[i]); } return 0; }