我是蒟蒻第一次写题解,希望这篇题解能给你帮助
核心解题思路
- 问题分析:由于每次操作会翻转自身及上下左右四个格子,且我们需要处理
的棋盘(
可达 100,
最大为 10)。直接暴力枚举所有状态是不可能的,但
意味着可以用一个整数表示一行的状态(二进制压缩)。
- 关键策略:
- 枚举第一行:第一行的操作方式决定了后续所有行的操作方式。第一行共有
种可能的操作状态(
,即最多 1024 种),这是算法能运行不超时的关键。
- 递推后续行:对于第
行,如果第
行的某个格子仍为白色(0),则必须在第
行的正下方位置进行翻转操作,才能将第
行的该位置变为黑色(1)。依此类推,逐行确定操作方案。
- 检查最后一行:处理完所有行后,只需检查最后一行是否全部变为黑色。如果是,则找到一个可行解。
- 目标:分别计算将棋盘变为全黑和全白的最小操作次数,取两者中的较小值。
- 时间复杂度:O(


)
参考代码 (C++)
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair<int,int> PII;
const int N=1e4+10;
int g[110][15];
int n,m;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
// 函数:计算将棋盘变为全x(1黑色,0白色)的最小操作次数
int cul(int x){
int min_ops=INT_MAX;
// 枚举第一行的所有操作状态 (0..2^m - 1)
for(int mask=0;mask<(1<<m);mask++){
int tmp[110][15];
int ops=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
tmp[i][j]=g[i][j];
}
}
//处理第一行的操作
for(int j=1;j<=m;j++){
if(mask&(1<<(j-1))){
ops++;
// 翻转第一行j列及其相邻格子
for(int i=0;i<4;i++){
int a=dx[i]+1,b=dy[i]+j;
if(a<1||a>n||b<1||b>m) continue;
tmp[a][b]^=1;
}
//翻转自己
tmp[1][j]^=1;
}
}
//处理第2到第n行
for(int i=2;i<=n;i++){
for(int j=1;j<=m;j++){
if (tmp[i - 1][j] == (1-x)) {//(1-x)意思是,如果要变成黑色1,那么要看哪个等于0;白色同理
ops++;
// 翻转当前行j列及其相邻格子
for (int d = 0; d < 4; d++) {
int ni = i + dx[d];
int nj = j + dy[d];
if (ni >= 1 && ni <= n && nj >= 1 && nj <= m) {
tmp[ni][nj] ^= 1;
}
}
// 自己也有被翻转
tmp[i][j] ^= 1;
}
}
}
// 检查最后一行是否全为x
bool ok = true;
for (int j = 1; j <= m; j++) {
if (tmp[n][j] != x) {
ok = false;
break;
}
}
if (ok) {
if (ops < min_ops) {
min_ops = ops;
}
}
}
return min_ops;
}
void solve() {
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char x;
cin>>x;
g[i][j]=x-'0';
}
}
int ans=min(cul(1),cul(0));//看看哪个更小
if(ans==INT_MAX){
cout<<"Impossible"<<endl;
}
else{
cout<<ans<<endl;
}
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t;
// cin>>t;
t=1;
while(t--) {
solve();
}
}