POJ2965 The Pilots Brothers' refrigerator (精妙方法秒杀DFS BFS)

这题以前做过,当时枚举翻转次数,搜过了,但是用了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以内了

POJ2965 The Pilots Brothers' refrigerator (精妙方法秒杀DFS BFS)_第1张图片

还有数组不要开的太小,我那几次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;
}


你可能感兴趣的:(ACM,poj)