题目:
输入样例:
-+--
----
----
-+--
输出样例:
6
1 1
1 3
1 4
4 1
4 3
4 4
首先理解下题意,下面是样例的运行过程:
分析:
算一下直接使用暴力的复杂度,已知4×4的矩阵共16个数,也就是起始可以改变的位置有16个,在每个起始状态下最坏会对16个数依次进行翻转。那么复杂度是
O
(
1
6
16
)
O(16^{16})
O(1616) 运行次数会超限制。可以考虑用二进制数
2
16
2^{16}
216的每一位的数值来标记16个数是否被翻转,也就是16位数的每一位有0/1两个状态(而起始状态仍为16个),这样复杂度会变成
O
(
16
×
2
16
)
O(16×2^{16})
O(16×216)
代码的过程理解如下:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
char a[5][5];//原始输入矩阵
char b[5][5];//中间变化后的矩阵
vector <pair<int,int>> ans;//记录最少翻转步骤
vector <pair<int,int>> tmp2;//中间翻转步骤
//翻转同行同列以及本身
void turn(int n)
{
int x=n/4+1;
int y=n%4+1;
tmp2.push_back(make_pair(x,y));//记录每种状态下的翻转步骤
for(int i=1; i<=4; i++)
{
b[x][i] = b[x][i] == '+' ? '-' : '+';//同行
b[i][y] = b[i][y] == '+' ? '-' : '+';//同列
}
b[x][y]=b[x][y]=='+'?'-':'+';//本身
}
//判断当前是否全部打开
int test()
{
for(int i=1; i<=4; i++)
for(int j=1; j<=4; j++)
if(b[i][j]=='+')
return 0;
}
int main()
{
for(int i=1; i<=4; i++)
for(int j=1; j<=4; j++)
cin>>a[i][j];
for(int i=1; i<(1<<16); i++)//每个初始状态对应的结果都要进行判断是否全部打开
{
memcpy(b, a, sizeof a); //将a的数据复制到b中
tmp2.clear();
int cnt=log(i)/log(2)+1;
for(int j=0; j<cnt; j++)
{
if((i>>j)&1)
turn(j);
}
//如果能全部打开并且所需步数更少,则更新解数组ans
if(test())
{
if(ans.size()==0||ans.size()>tmp2.size())
ans=tmp2;
}
}
cout<<ans.size()<<endl;
for(int i=0; i<ans.size(); i++)
printf("%d %d\n",ans[i].first,ans[i].second);
return 0;
}
/*
输入:
-+--
----
----
-+--
*/
琐碎的知识点补充:
(1)关于vecter的使用详细介绍看https://blog.csdn.net/msdnwolaile/article/details/52708144
(2)memcpy(b, a, sizeof a);的介绍:
void *memcpy(void *destin, void *source, unsigned n);
作用是:以source指向的地址为起点,将连续的n个字节数据,复制到以destin指向的地址为起点的内存中。