题目描述:
“飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱。
已知每个把手可以处于以下两种状态之一:打开或关闭。
只有当所有把手都打开时,冰箱才会打开。
把手可以表示为一个4х4的矩阵,您可以改变任何一个位置[i,j]上把手的状态。
但是,这也会使得第i行和第j列上的所有把手的状态也随着改变。
请你求出打开冰箱所需的切换把手的次数最小值是多少。
输入格式
输入一共包含四行,每行包含四个把手的初始状态。
符号“+”表示把手处于闭合状态,而符号“-”表示把手处于打开状态。
至少一个手柄的初始状态是关闭的。
输出格式
第一行输出一个整数N,表示所需的最小切换把手次数。
接下来N行描述切换顺序,每行输入两个整数,代表被切换状态的把手的行号和列号,数字之间用空格隔开。
数据范围
1≤i,j≤4
输入样例:
-+--
----
----
-+--
输出样例:
6
1 1
1 3
1 4
4 1
4 3
4 4
注意:如果存在多种打开冰箱的方式,则按照优先级整体从上到下,同行从左到右打开。
看到这道题以为和费解的开关类似,用那个方法做了一遍不对,这道题因为只有16个门把手,所以枚举16中情况就行,用一个二进制为16位的数来表示每个开关的情况,复杂度为O(n^16),
具体解释标注在代码里了
AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdlib.h>
#include<stack>
#include<vector>
#include<queue>
const int INF = 0x3f3f3f3f;
using namespace std;
int a[21][21],i,j,k;
queue<pair<int,int> > ans1;
queue<pair<int,int> > kong; //空序列,用于清空序列
int main()
{
char ch;
for (i=1; i<=4; i++)
{
for (j=1; j<=4; j++)
{
ch=getchar();
if (ch=='-')
a[i][j]=1;
}
getchar();
}
int ans=1e7;
for (i=1; i<(1<<17); i++)
{
int b[21][21];
memcpy(b,a,sizeof a );
ans1=kong;
for (j=1; j<=16; j++)
if (i>>(j-1) & 1)//取出i的每一位
{
int dx=j%4,dy=j/4+1;//当前的行列位置
if (j%4==0)
dx=4,dy--;
for (k=1;k<=4;k++)
b[dy][k]^=1;
for (k=1;k<=4;k++)
if (k!=dy)//特判避免当前点重复操作
b[k][dx]^=1;//每一行每一列进行相反操作,
ans1.push(make_pair(dy,dx));//ans1来储存操作的位置
}
bool ok=true;
for (j=1;j<=4;j++)
for (k=1;k<=4;k++)
if (!b[j][k])
ok=false;
if (ok)
break;
}
cout<<ans1.size()<<endl;
while(!ans1.empty())
{
cout<<(ans1.front()).first<<" "<<ans1.front().second<<endl;
ans1.pop();
}
return 0;
}