POJ 2965 The Pilots Brothers' refrigerator(枚举+DFS)
原题地址
http://poj.org/problem?id=2965
题意:给定4*4矩阵,每个方格为’+’或’-‘,对每个方格状态的翻转都会引起所在行、所在列其它方格的翻转,求最小的翻转次数和翻转序列,使得所有方格都为’-‘。
解题思路
本题与前一篇博文《POJ 1753 Flip Game》的题目非常像,都是枚举相关的题目。
在POJ 1753里用到了状态压缩的方法,状态压缩的核心思想就是用二进制位来表示对应的位置的两种状态,整个4*4数组就可以用一个二进制数表示。这道题同样可以采用状态压缩的办法,但是编写代码时不太方便,也不太好理解,暂时用一般的数组方法来表示。
由于本题要求输出最优解的完整序列,因此采用带记录路径的DFS+回溯:
- 判断当前数组的情况是否满足开锁条件(即全为’-‘),如果满足,再这趟成功搜索所需的翻转次数Step,是否比之前解锁所需的最少次数minStep更小。同时将这趟搜索的临时路径保存到最终路径中。
- 在对每个位置处理时,要么不翻转该位置继续向下搜索,不改变翻转次数;要么翻转该位置后再向下搜索,记录翻转的位置,同时翻转次数加一。无非是这两种情况,用二叉树的观点来看,左子树代表不翻转,右子树代表翻转。
- 回溯思想的体现:某个位置处理完之后回溯到上一个位置,对上一个位置其他情况继续搜索。
- 结构体PATH的数组用于记录每趟DFS的路径,tmp存放翻转经过的临时路径, ans存放最终的路径。
AC代码
#include <iostream>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef struct node
{
int x, y;
}PATH;
int handle[4][4]; //把手的开关情况
PATH tmp[20], ans[20];//tmp存放翻转经过的临时路径, ans存放最终的路径
int minStep = 32; //开锁需要的最小次数
void Init()
{
memset(handle, 0, sizeof(handle));
char c;
for (int i = 0; i<4; ++i)
for (int j = 0; j<4; ++j)
{
cin >> c;
if(c == '+') //某位置有锁,标记为1
handle[i][j] = 1;
}
}
bool IsOpened() //判断是否开了锁
{
for (int i = 0; i<4; ++i)
for (int j = 0; j<4; ++j)
{
if (handle[i][j] == 1) //还存在锁
return false;
}
return true;
}
void flip(int num) //翻转第num(0~15)个位置的锁
{
int row = num/4, col = num%4;
handle[row][col] = !handle[row][col]; //该锁自身取反
for (int i = 0; i<4; ++i)
{
handle[row][i] = !handle[row][i]; //所在行取反
handle[i][col] = !handle[i][col]; //所在列取反
}
}
void DFS(int curr, int step) //curr当前要处理的节点,step处理到当前节点共翻转了几次
{
if (IsOpened() == true) //已经能开锁
{
if (step < minStep) //更新最小次数
{
minStep = step;
for (int i = 0; i<step; ++i) //更新当前最小次数的由来路径
ans[i] = tmp[i]; //按字节复制
}
return;
}
if (curr > 15) return; //搜索失败,越界
//对每个位置的可能情况做深搜,最后回溯(左子树为不翻转,右子树为翻转)
//不翻转该位置,继续向下搜索
DFS(curr+1, step);
//翻转该位置,同时记录这次翻转的位置
flip(curr);
tmp[step].x = curr/4+1;
tmp[step].y = curr%4+1;
DFS(curr+1, step+1); //继续向下搜索
flip(curr); //回溯
return; //回到根节点
}
int main()
{
Init();
DFS(0, 0);
cout << minStep << endl; //输出最小次数
for (int i = 0; i<minStep; ++i) //输出最终路径
cout << ans[i].x << ' ' << ans[i].y << endl;
return 0;
}
内存占用: 712K 耗时:422ms(状态压缩会快一些)