这题以前做过,当时枚举翻转次数,搜过了,但是用了800+ms,看着人家都两位数甚至0ms,我再次回顾了下这题,虽然这次的精妙方法不是我自己想出来的(毕竟精妙,我还是差的很远呢)
首先这题BFS或者递归枚举的思路都是特别容易TLE的,不TLE的人也是爬过,而且用了不少的修改。
DFS虽然可以优化到二三百ms,但是我不是很认可DFS啊,明明求得是最少的次数,而查到的DFS的代码都是搜到一个解救输出了,感觉不是太对,所以不提。
还有discuss里看到了高斯消元,原谅本渣不懂什么叫高斯消元。
这个方法也是discuss里的高人想出来了,那个上面讲的不是太清楚,所以我再好好的说一下。
首先要知道这个操作,操作偶数次也等于没有操作,操作奇数次等于操作1次,所以状态可以变成0和1,0为没有翻转,没操作,1为操作一次。
其次要证明,如果有一个+,怎么样才能把它翻转到-
要把这个+所在的行和列一共7个位置,全部操作一次,其余6个位置都是翻转了4次,保持不变,而中心的+所在的位置翻转了7次,变为-,如此操作就全都是-了
现在有若干个+,要把他们全部翻成-,就可以这样思考,把这些+逐个操作成-,因为操作这个+所在的行和列,除了中心的符号会变,周围的都不会改变。
所以可以这样想,如果有3个+,你先把第一个+的行列一共7个位置都操作一次,这个+就没了,然后再对第二个+的行列一共7个位置操作一次,以此类推。
这样可以看到,有些位置操作了偶数次,等同于这些位置可以不用操作,操作奇数次的位置等同于操作1次,这样就是最少步骤了(因为每两次操作等于无用功)
这样就归纳出了本题的方法,开一个4*4的数组,全为0,如果输入的矩阵中某位置为+,就把这个位置的行列7个全部操作一次(取反即可0变1,1变0)
把所有的+全部操作完之后,看这个数组中有多少个1,就是需要做多少次操作,为1的位置就是需要操作的坐标。
这样时间复杂度就大大滴降低了,肯定在100ms以内了
还有数组不要开的太小,我那几次WA都是正好开了4啊16啊
#include<iostream>
#include<cstdio>
using namespace std;
int main(){
char s[5][5];
int mark[5][5];
int x[20],y[20];
memset(mark,0,sizeof(mark));
for(int i=0;i<4;i++) cin>>s[i];
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(s[i][j]=='+'){
mark[i][j]=!mark[i][j];//下面的操作中,i,j这个位置翻转了操作了两次,等于没操作,所以上边这里再操作一次,这样让这个位置 也被操作了
for(int k=0;k<4;k++){
mark[i][k]=!mark[i][k];
mark[k][j]=!mark[k][j];
}
}
}
}
int ans=0;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(mark[i][j]){
x[ans]=i+1;
y[ans]=j+1;
ans++;
}
}
}
printf("%d\n",ans);
for(int i=0;i<ans;i++) printf("%d %d\n",x[i],y[i]);
return 0;
}