题目背景
上元佳节,庙会里举办着各式各样的庆典活动,牛宝也兴奋地参与其中。突然,他被一个新颖的点灯游戏所吸引,游戏要求最终点亮所有在场的花灯,每盏灯都有开关两种状态,每一次点击在场的一盏任意状态的花灯,其自身和四周的的花灯都会改变其开关状态。
例如
3*3的花灯阵
0 1 1
1 0 0
1 0 1
点一下最中间的灯【2,2】就变成了
0 0 1
0 1 1
1 1 1
再点一下左上角的灯【1,1】就变成了
1 1 1
1 1 1
1 1 1
最终输出点亮花灯的序号(从左上角到右下角序号依次为1-9),答案即点亮1号灯和5号灯。 为了奖品,牛宝只能尝试破解3*3的花灯阵,你能协助他吗?
题目描述
给出3*3的花灯阵各盏花灯的初始开关状态,请你按照序号从小到大的顺序点亮花灯(从左上角到右下角序号依次为1-9),同时要求点亮的次数最少(避免多余的点亮步骤),使得最终全部花灯均被点亮。
输入格式
九个数字,3*3的格式输入,每两个数字中间只有一个空格,表示灯初始的开关状态。(0表示关,1表示开)
输出格式
按照序号从小到大的顺序点亮花灯(从左上角到右下角序号依次为1-9),输出点亮的序号集合(每两个序号之间用空格隔开)。
输入输出样例
输入
0 1 1
1 0 0
1 0 1
输出
1 5
说明/提示
牛宝正在脑海中不停地进行尝试。。。
解题思路:
从左上到右下1-9的顺序进行点亮深搜,一旦点亮该灯,自身和其四周灯亮灭情况变化,保留点亮过程灯号;
深搜这条路径无法点亮所有的灯则进行回溯,自身和其四周灯亮灭情况变化。
深搜边界条件即点亮所有的灯,一旦有更少步数的进行更新方案。
代码如下:
#include <iostream>
using namespace std;
const int N = 5;
int g[N][N];
int ans = 10;
int path[10];
bool vis[10];
int paths[10];void turn(int x, int y) { //开关灯g[x][y] = 1 - g[x][y];g[x - 1][y] = 1 - g[x - 1][y];g[x + 1][y] = 1 - g[x + 1][y];g[x][y + 1] = 1 - g[x][y + 1];g[x][y - 1] = 1 - g[x][y - 1];
}void dfs(int n) {if (n > ans)return ;//剪枝,不剪枝会超时//超过最小的次数就返回,初始次数为10,规律:它总共步骤不会超过9次int sum = 0;for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++)sum += g[i][j];if (sum == 9) {ans = min(ans, n - 1);for (int i = 1; i <= ans; i++) {paths[i] = path[i];}return ;}for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++) {turn(i, j);path[n] = (i - 1) * 3 + j;//将二维数组转换为一维dfs(n + 1);turn(i, j);}}int main() {for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++)cin >> g[i][j];dfs(1);for (int i = 1; i <= ans; i++) {cout << paths[i] << " ";}return 0;
}
当然,因为按2下等于没有按,所以我们可以增加一个标记数组,这样就会少按很多重复的无效的次数,相当于剪枝。
代码如下:
#include <iostream>
using namespace std;
const int N = 5;
int g[N][N];
int ans = 10;
int path[10];
int paths[10];
bool vis[N][N];void turn(int x, int y) { //开关灯g[x][y] = 1 - g[x][y];g[x - 1][y] = 1 - g[x - 1][y];g[x + 1][y] = 1 - g[x + 1][y];g[x][y + 1] = 1 - g[x][y + 1];g[x][y - 1] = 1 - g[x][y - 1];
}void dfs(int n) {if (n > ans)return ;//剪枝,不剪枝会超时//超过最小的次数就返回,初始次数为10,规律:它总共步骤不会超过9次int sum = 0;for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++)sum += g[i][j];if (sum == 9) {ans = min(ans, n - 1);for (int i = 1; i <= ans; i++) {paths[i] = path[i];}return ;}for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++) {if (!vis[i][j]) {vis[i][j] = true;turn(i, j);path[n] = (i - 1) * 3 + j;//将二维数组转换为一维dfs(n + 1);vis[i][j] = false;turn(i, j);}}}int main() {for (int i = 1; i <= 3; i++)for (int j = 1; j <= 3; j++)cin >> g[i][j];dfs(1);for (int i = 1; i <= ans; i++) {cout << paths[i] << " ";}return 0;
}
当然,如果你想不到怎么把二维数组转换为一维,也可以这样写:
#include<bits/stdc++.h>
using namespace std;
int s[5][5];int book[5][5];int small=10;
int t[10];
int tt[10];
int change(int x,int y)
{s[x][y]=1-s[x][y];s[x-1][y]=1-s[x-1][y];s[x+1][y]=1-s[x+1][y];s[x][y-1]=1-s[x][y-1];s[x][y+1]=1-s[x][y+1];return 0;
}
int dfs(int k)
{if(k>small)return 0;int sum=0;for(int i=1;i<=3;i++){for(int j=1;j<=3;j++){sum+=s[i][j];}}if(sum==9){if(k<small){small=k;for(int i=1;i<=k;i++){tt[i]=t[i];}}return 0;}for(int i=1;i<=3;i++){for(int j=1;j<=3;j++){if(book[i][j]==0){if(i==1&&j==1)t[k+1]=1;if(i==1&&j==2)t[k+1]=2;if(i==1&&j==3)t[k+1]=3;if(i==2&&j==1)t[k+1]=4;if(i==2&&j==2)t[k+1]=5;if(i==2&&j==3)t[k+1]=6;if(i==3&&j==1)t[k+1]=7;if(i==3&&j==2)t[k+1]=8;if(i==3&&j==3)t[k+1]=9;change(i,j);book[i][j]=1;dfs(k+1);change(i,j);book[i][j]=0;} }}return 0;
}
int main()
{for(int i=1;i<=3;i++){for(int j=1;j<=3;j++){cin>>s[i][j];}}dfs(0);for(int i=1;i<=small;i++){cout<<tt[i]<<" ";}return 0;
}