费解的开关

AcWing

题意:给出一个\(5×5\)矩形网格图,\(a[i][j]\)表示第i行第j列的数字(只能为0或者1),每次操作可以选择一个位置,对于该个位置以及其上下左右个一个位置上的数字0变成1,1变成0,询问是否能少于6次将所有数字变为1,如果能,请输出最少次数,否则输出“-1”.

分析:思路一:倒序BFS:把每个状态看成一个25位的二进制数,从最终状态也就是全部位都为1开始BFS,把6步之内能走到的状态用map记下,就可以\(O(1)\)回答每个询问了.但是BFS会超时...但还是放一下代码,看看能不能优化.

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
map<int,int>Map;
queue<int>q;
inline int get_map(int x,int i){//对于状态x,我按下第i位,会变成什么状态?
    x=x^(1<<i);
    if((i%5)<4)x=x^(1<<(i+1));
    if(i%5)x=x^(1<<(i-1));
    if(i>=5)x=x^(1<<(i-5));
    if(i<20)x=x^(1<<(i+5));
    return x;
}
inline void bfs(){
	q.push((1<<25)-1);Map[(1<<25)-1]=0;
	while(!q.empty()){
		int now=q.front();q.pop();
		if(Map[now]==7)return;
		for(int i=0;i<25;++i){
			int res=get_map(now,i);
			if(!Map[res]){
				Map[res]=Map[now]+1;
				q.push(res);
			}
		}
	}
}
int main(){
	bfs();
	int T=read();
	while(T--){
		int cnt=0;
		for(int i=0;i<25;++i){
			char ch;cin>>ch;
			cnt+=((ch-'0')<<i);
		}
		if(Map[cnt])printf("%d\n",Map[cnt]);
		else puts("-1");
	}
    return 0;
}

思路二:DFS:挖掘题目性质,若我们固定了第一行,则点击方案唯一,因为若第二行某一位为0,只能通过点击第三行的该位来改变,每一行这样递推下去,如果最后一行的状态不全为1,说明本次对第一行的点击方式不合法.综上,所以我们DFS对第一行点击方式的枚举,然后每次check这次点击方式是否合法就行了,如果合法就比较最小步数.

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
int ans,a[10][10],b[10][10];
inline int check(int now){
    int sum=now;
    for(int i=1;i<=5;i++)
		for(int j=1;j<=5;j++)
			b[i][j]=a[i][j];
    for(int i=1;i<=4;i++)
		for(int j=1;j<=5;j++)
			if(!b[i][j]){
				++sum;
				b[i][j]^=1;
				b[i+1][j]^=1;
				b[i+1][j-1]^=1;
				b[i+1][j+1]^=1;
				b[i+2][j]^=1;
			}
    for(int i=1;i<=5;i++)if(!b[5][i])return 1e9; 
    return sum;
}
inline void dfs(int lie,int now){
    if(lie>5){
        ans=min(ans,check(now));
        return;
    }//第一行点击方式枚举完毕
    a[1][lie]^=1;a[1][lie-1]^=1;a[1][lie+1]^=1;a[2][lie]^=1;
    dfs(lie+1,now+1);//点击第一行第lie列,步数now+1
    a[1][lie]^=1;a[1][lie-1]^=1;a[1][lie+1]^=1;a[2][lie]^=1;
    dfs(lie+1,now); //回溯,也就是不点击第一行第lie列
    return;
}
int main(){
	int T=read();
	while(T--){
		for(int i=1;i<=5;++i)
			for(int j=1;j<=5;++j){
				char ch;cin>>ch;
				a[i][j]=ch-'0';
			}
		ans=1e9;dfs(1,0);
        if(ans<=6)printf("%d\n",ans);
		else puts("-1");
	}
    return 0;
}

posted on 2019-07-27 08:57  PPXppx  阅读(134)  评论(0编辑  收藏  举报