题目
思路:纯暴力二进制枚举
我们知道,一个点,改变状态两次等于没改变
所以在求最小改变次数的时候,每个点是最多只改变一次的,因此我们可以用位运算遍历出这所有的情况
16个位置,每个位置有两种情况,遍历每一种情况,可以看成从0到216-1的遍历
每个数字的二进制表示:1表示要改变状态,0表示不要改变状态
时间复杂度为:
而要求以字典序输出,其实按从小到大二进制枚举,一定就是按字典序输出改变
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int,int> PII;
const int N=5;
#define x first
#define y second
char g[N][N],backup[N][N];
int get(int i,int j) //二维转一维
{
return 4*i+j;
}
void turn_one(int x,int y)
{
if(g[x][y]=='-') g[x][y]='+';
else g[x][y]='-';
}
void turn_all(int x,int y)
{
for(int i=0;i<4;i++)
{
turn_one(x,i);
turn_one(i,y);
}
turn_one(x,y);
}
int main()
{
for(int i=0;i<4;i++) cin>>g[i];
memcpy(backup,g,sizeof(g)); //备份
vector<PII> res; //保存答案
for(int op=0;op<1<<16;op++) //遍历0-2的16次方-1
{
vector<PII> temp; //存储当前情况下的操作
for(int i=0;i<4;i++) //遍历当前所有开关
for(int j=0;j<4;j++)
if(op>>get(i,j)&1) //判断是否需要改变
{
turn_all(i,j); //进行改变
temp.push_back({i,j}); //记录操作
}
bool has_closed=false;
for(int i=0;i<4;i++) //检测当前情况下是否能使所有开关打开
for(int j=0;j<4;j++)
if(g[i][j]=='+')
{
has_closed=true;
break;
}
if(!has_closed)
{ //记录次数最少的操作
if(res.empty()||res.size()>temp.size()) res=temp;
}
memcpy(g,backup,sizeof(backup)); //恢复
}
printf("%d\n",res.size());
for(auto op:res) printf("%d %d\n",op.x+1,op.y+1);
return 0;
}