思路:
1.每个开关只用开一次,开偶数次没有用
2.顺序不影响结果
3.第一层为暗,要变亮它的下一层对应开关必须按,
即为每一层开关的状态由上一层唯一确定。
4.需要的总次数:固定第一行,枚举第一行开关的所有可能性,每个开关都是按或者不按,共2^5种,每种可能对应的情况都算一次,按3成立的条件进行,而且要保证最后第五行灯全亮,则记录次数,最后找最小次数.5.详细的枚举递归思路:
核心:枚举第一层开关的所有状态,每个开关都有两种状态按或者不按。同时需要记录当前的开关按了的次数,还有当前开关数组的状态,因为这俩都需要记录并恢复到上一层。之后对于开关整体第一层的每种确定好的状态进行2-5层的遍历判断是否可行即可。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; /*思路:1.每个开关只用开一次,开偶数次没有用 2.顺序不影响结果 3.第一层为暗,要变亮它的下一层对应开关必须按, 即为每一层开关的状态由上一层唯一确定。 4.需要的总次数:固定第一行,枚举第一行开关的所有可能性,共2^5种,每种可能对应的情况 都算一边,按3成立的条件进行,而且要保证最后第五行灯全亮,则记录次数,最后找处最小次数.*/ typedef struct { char g[6][6]; }sq; sq a[501]; char change(char x) { int x1=x-'0'; char ans; if(x1==0) ans='1'; if(x1==1) ans='0'; return ans; } int min1=9999; void anse(sq a,int x,int an)//a表示全部的数组,x表示第一行枚举到第几位,an记录每次的开关按动次数 //递归恢复的状态永远是前一个状态,例如当x+1>5输出时原开关数组改变了 ,但后面return到x的上一层即x=4的下面一步an++,此时开关数组未变之后仍正常走 //当前开关按的次数an也是,永远只跟前一个状态有关,即return到的那个状态 ,故按下时an++,按下完return上一层时再an--; { //第一列所有的可能,指数型枚举 if(x>5) { //固定一列后的枚举过程 for(int i=2;i<=5;i++)//行 { for(int j=1;j<=5;j++)//列 { if(a.g[i-1][j]=='0')//上一层为暗,这次必须亮 { a.g[i][j]=change(a.g[i][j]);//上下左右都变色 a.g[i-1][j]=change(a.g[i-1][j]); a.g[i+1][j]=change(a.g[i+1][j]); a.g[i][j-1]=change(a.g[i][j-1]); a.g[i][j+1]=change(a.g[i][j+1]); an++; } } } int flag=0; for(int j=1;j<=5;j++)//判断第五行是否全亮 { if(a.g[5][j]=='0') flag=1; } if(flag==0&&an<=6&&an<min1) { min1=an; } return; } //x<=5 anse(a,x+1,an); an++; a.g[1][x]=change(a.g[1][x]); a.g[2][x]=change(a.g[2][x]); a.g[1][x-1]=change(a.g[1][x-1]); a.g[1][x+1]=change(a.g[1][x+1]); anse(a,x+1,an); an--; } int main() { int n; cin>>n; for(int i=1;i<=n;i++) { for(int j=1;j<=5;j++) { for(int k=1;k<=5;k++) { cin>>a[i].g[j][k]; } } } for(int i=1;i<=n;i++) { min1=999;//初始化最小值 anse(a[i],1,0); if(min1==999)cout<<"-1"<<endl;//无解 else {cout<<min1<<endl; } } return 0; }
费解的开关
于 2024-03-31 15:57:30 首次发布