题目链接:点我啊╭(╯^╰)╮
题目大意:
的图, 为开灯, 为关灯,每次选择翻转图中的一点时,它包括相邻的四个点都要翻转,问能否能否全为 ,并输出全为 时,翻转次数最少 且 字典序最小的方案???
解题思路:
和一般的搜索不同,DFS和BFS都不行,一开始也许感觉无从下口,但根据它要求字典序最小,可以联想到状态压缩,当然这题显然不是状压,而是枚举。并且有一个前提,每一个点最多只能翻一次,两次则会重复。
我们枚举搜索,怎么搜索呢、?只能从第一行开始搜索,假设我们将第一行已经确定了,那么从第二行开始翻转时,必须满足上一行对应的点为
才能翻转(一行确定的意思并不代表上一行全为
),这样只要确认了第一行,第二行也会被确定,以此类推…
代码思路:
状态枚举第一行,逐个进行判断,记录翻转次数最小并且字典序最小的方案~
核心:灵活的理解搜索这思想,用枚举的方法来解决,最重要的是运用 确定了上一个状态,则解决了下一个状态的思想!!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int m, n, fnum, num=INT_MAX;
int mp[20][20], a[20][20], f[20][20], ans[20][20];
void flip(int x, int y) { //翻转
fnum++;
f[x][y] = 1;
a[x][y] = 1-a[x][y];
a[x-1][y] = 1-a[x-1][y];
a[x+1][y] = 1-a[x+1][y];
a[x][y-1] = 1-a[x][y-1];
a[x][y+1] = 1-a[x][y+1];
}
int main() {
scanf("%d%d", &m, &n);
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
scanf("%d", &mp[i][j]);
for(int t=0; t<(1<<n); t++) { //第一行所有情况
for(int i=1; i<=m; i++)
for(int j=1; j<=n; j++)
a[i][j] = mp[i][j];
fnum = 0;
int move = t;
memset(f, 0, sizeof(f));
for(int i=1; i<=n; i++) { //翻转第一行
if(move&1) flip(1, i);
move>>=1;
}
for(int i=2; i<=m; i++) //翻转后面
for(int j=1; j<=n; j++)
if(a[i-1][j]) flip(i,j);
int i;
for(i=1; i<=n; i++) //判断最后一行
if(a[m][i]) break;
if(fnum<num && i==n+1) { //记录
num = fnum;
for(i=1; i<=m; i++)
for(int j=1; j<=n; j++)
ans[i][j] = f[i][j];
}
}
if(num==INT_MAX) printf("IMPOSSIBLE\n");
else {
for(int i=1; i<=m; i++) {
for(int j=1; j<=n; j++)
if(j==1) printf("%d", ans[i][j]);
else printf(" %d", ans[i][j]);
printf("\n");
}
}
return 0;
}