核心
-
枚举第一行,指的是第一行哪些位置要切换状态!!!。第一行总共有5个数,组合数是32,即第一行哪些位置要切换总共有32种情况。这就是我们的枚举空间。比如,k = 0 表示第一行就这样,不切换状态。k = 2,二进制是 00010,就表示 数组g[][]的第一行第2个灯要切换状态。
-
这里的逻辑是:完成一件事A –> C ,拆分成两步,先从A –> B ,再从B – >C ,总共需要的花费就是两步之和。这里的A –> B 就是从g[][]变成第一行的新状态,需要的反转次数;这里的B – >C就是从第一行的新状态开始往下翻转,直到所有的灯都亮需要的反转次数。
采用的测试用例
00111
01011
10001
11010
11100
#include<bits/stdc++.h>
using namespace std;
const int INF = 100000000;
char g[10][10];
int dx[5] = {0, -1, 0, 1, 0}, dy[5] = {0, 0, 1, 0, -1};
// 反转灯
void turn(int x, int y){
for(int i = 0; i < 5; i ++){
int a = x + dx[i], b = y + dy[i];
if( a >= 0 && a < 5 && b >=0 && b < 5){
g[a][b] ^= 1;// 48 变成 49 即 '0'变成'1','1'变成'0'
}
}
}
int work(){
int ans = INF;
// 枚举第一行的状态,固定第一行,逐层往下递推
// 第一行共有32种状态
for(int k = 0; k < 1 << 5; k ++){
int res = 0; // 统计翻转次数
char backup[10][10];
memcpy(backup, g, sizeof g); // g数组copy到backup数组中
// 把g[][]数组按照枚举的第一行状态进行更改
// 然后依次往下递推,以便找到最少的步数。
for(int j = 0; j < 5; j ++){
if( k >> j & 1){
res ++;
turn(0, j);
}
}
//逐行递推
for(int i = 0; i < 4; i++)
for(int j = 0; j < 5; j++){
if( g[i][j] == '0'){
res ++;
turn(i + 1 , j);
}
}
// 看最后一行是否全1
bool is_successful = true;
for(int i = 0; i< 5; i ++)
if(g[4][i] == '0'){
is_successful = false;
break;
}
if(is_successful) ans = min(ans, res);
memcpy(g, backup, sizeof backup);
}
if( ans > 6) ans = -1;
return ans;
}
int main(){
int T;
cin >> T;
while( T --){
for(int i = 0; i < 5; i ++) cin >> g[i];
cout << work() << endl;
}
}
你咋那么强,可以带带吗,太难了。
%%%
csdn博文地址:
https://lishizheng.blog.csdn.net/article/details/115087616