0 题目来源
1 涉及的知识点
指数型枚举、递归、暴搜
指数型枚举:
见递归实现指数型枚举题目
注意指数型枚举不需要for循环。
2 题目描述
“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有 1616 个把手的冰箱。
已知每个把手可以处于以下两种状态之一:打开或关闭。
只有当所有把手都打开时,冰箱才会打开。
把手可以表示为一个 4×4的矩阵,您可以改变任何一个位置 [i,j][i,j] 上把手的状态。
但是,这也会使得第 ii 行和第 jj 列上的所有把手的状态也随着改变。
请你求出打开冰箱所需的切换把手的次数最小值是多少。
3 输入输出
输入格式:
输入一共包含四行,每行包含四个把手的初始状态。
符号 + 表示把手处于闭合状态,而符号 - 表示把手处于打开状态。
至少一个手柄的初始状态是关闭的。
输出格式:
第一行输出一个整数 NN,表示所需的最小切换把手次数。
接下来 NN 行描述切换顺序,每行输出两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。
注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。
数据范围:
1≤i,j≤41≤i,j≤4
4 样例
输入样例:
-+—
——
——
-+—
输出样例:
6
1 1
1 3
1 4
4 1
4 3
4 4
5 思路
由于本题中改变一个开关,会导致一行一列的开关变化,变化的范围较大,使用递推不太现实。
因此,我们可以直接对44的地图进行暴力搜索。
共有44=16个开关,每个开关有选或不选两种情况,因此总共有种情况,暴搜是完全可以的。
对16个开关进行二元遍历,是典型的指数型枚举,采用递归实现。注意递归实现指数型枚举的形式(没有for循环)
本题中为了方便检测开关是否全部打开,采用opennum变量对开关打开数目进行记录,并在改变开关状态的过程中随时记录。
6 代码
#include<iostream>#include<string>#include<vector>using namespace std;struct xy{int x, y;};string str[4];int opennum;//记录打开开关的数目int minstep = 1000000;vector<xy> vecxy;vector<xy> finalvec;void exc(int x, int y)//改变开关状态{for (int i = 0; i < 4; i++){if (str[x][i] == '+'){str[x][i] = '-';opennum++;}else{str[x][i] = '+';opennum--;}if (i != x)//避免中间开关被重复改变{if (str[i][y] == '+'){str[i][y] = '-';opennum++;}else{str[i][y] = '+';opennum--;}}}}void dfs(int u,int s){if (u == 16){if (opennum == 16 && s < minstep){minstep = s;finalvec = vecxy;}return;}int j = u % 4;int i = u / 4;xy temp;temp.x = i + 1;temp.y = j + 1;vecxy.push_back(temp);exc(i, j);dfs(u + 1, s + 1);//选vecxy.pop_back();exc(i, j);dfs(u + 1, s);//不选}int main(){for (int i = 0; i < 4; i++){cin >> str[i];for (int j = 0; j < 4; j++){if (str[i][j] == '-')opennum++;}}dfs(0, 0);cout << minstep << endl;for (int i = 0; i < finalvec.size(); i++){cout << finalvec[i].x << ' ' << finalvec[i].y << endl;}return 0;}
