题目理解:4x4=16个格子,每个格子有两种颜色:黑(b)或白(w),给出初始颜色后,每次可翻转一个格子,即由黑变白,或由白变黑。每翻转一个格子时,其周围的四个格子(即上下左右四个)也要一起翻转。输入为4x4矩阵,表示格子的初始颜色,输出为使得所有格子翻转为同一颜色所需要的翻转的最小格子数目或Impossible
思路变迁:
- void backtrack(int t)
- {
- if(t > n)
- output(x);
- for(i=0; i<=1; i++) {
- x[t] = i;
- if(constraint(t) && bound(t))
- backtrack(t+1);
- }
- }
可以看到x[n]数组即解空间,数组每一个位置可为0或1,constraint(t)为约束函数,bound(t)为限界函数,可用其对子集树进行剪枝、
结合本题所需的改变:1)x[n]数组值为1,表示翻转,值为0,表示不翻转;2)只要翻转为同一颜色即代表成功,所以搜索成功并不只在叶节点;3)为了表示搜索成功,回溯函数需要另一个参数;4)需要记录最小翻转次数,所以需要对解空间每个节点即x[n]数组,统计其中为1(表明翻转)的个数;5)翻转过的节点回溯回来时,再翻转一次,即为原始的值
4、加速你的算法:1)4x4矩阵变数字,依次读入,第i位置的权值都为2的15-i次方;2)翻转变为位运算,使用异或,与0异或保持不变,与1异或相当于取反;3)空间换时间,提前计算每个位置翻转进行异或所需的数字,用数组保存
综合以上,代码如下:
- #include<stdio.h>
- void backtrack(int sum, int t);
- int find=0;
- int min=16;
- int mypow[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
- int flip[16]={51200,58368,29184,12544,35968,20032,10016,4880,2248,1252,626,305,140,77,39,19};
- int x[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
- int main()
- {
- int i=0;
- char a;
- int sum = 0;
- for(i=0; i<=15; ) {
- scanf("%c", &a);
- if(a=='b') {
- sum += mypow[15-i];
- i++;
- }
- else if(a == 'w')
- i++;
- }
- backtrack(sum, 0);
- if(find == 0)
- printf("Impossible\n");
- else
- printf("%d\n", min);
- return 0;
- }
- void backtrack(int sum, int t)
- {
- int i;
- int result=x[0];
- for(i=1; i<=15; i++) {
- result+=x[i];
- }
- if(sum == 65535 || sum == 0)
- {
- find=1;
- if(result < min)
- min = result;
- }
- if(t == 16)
- return;
- for(i=0; i<=1; i++) {
- x[t] = i;
- if(i==0)
- backtrack(sum, t+1);
- else
- backtrack(sum^flip[t], t+1);
- }
- }
5、使用迭代来代替递归,考虑子集树回溯的迭代模板:
- x[n]={-1};//初始化
- t=0;
- while(t>=0) {
- while(x[t]<=1) {
- x[t]++;
- if(x[t]<=1) {
- if x此时为最终的合法解
- 记录或输出
- else if x为部分解
- t=t+1;
- }
- }
- x[t]=-1;或其他恢复操作
- t--;//回溯
- }
- #include<stdio.h>
- using namespace std;
- bool find=false;
- int min=16;
- int mypow[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
- int flip[16]={51200,58368,29184,12544,35968,20032,10016,4880,2248,1252,626,305,140,77,39,19};
- int x[16]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
- int main()
- {
- int i=0, j=0;
- char a;
- int sum = 0;
- int result, t;
- for(i=0; i<=15; ) {
- scanf("%c", &a);
- if(a=='b') {
- sum += mypow[15-i];
- i++;
- }
- else if(a == 'w')
- i++;
- }
- while(t>=0) {
- while(x[t]<=1) {
- x[t]++;
- if(x[t] == 1) {
- sum = sum^flip[t];
- }
- if(x[t]<=1) {
- if(t==15) {
- for(j=1, result=x[0]; j<=15; j++)
- result += x[j];
- if(sum == 65535 || sum == 0) {
- find = true;
- if(result < min)
- min = result;
- }
- } else {
- t++;
- }
- }
- }
- x[t] = -1;
- sum = sum^flip[t];
- t--;
- }
- if(find)
- printf("%d\n", min);
- else
- printf("Impossible\n");
- return 0;
- }