开关问题满足的条件:
- 最优解情况下,一个开关只按一次
- 开关的顺序无关
由于数据范围比较小,可以直接枚举出所有开关情况(0~2^16 - 1),然后操作一下灯泡,然后检查一下是不是全开开了即可,注意最后答案要求字典序最小的步数最少的解。
之所以从0~2^16枚举是因为后面一个数中包含的1的个数一定>= 前面一个数的1的个数,所以从小到大找到的第一个解就是答案。
暴力代码
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
#define PII pair<int, int>
const int N = 10;
char g[N][N], backup[N][N];
void turn(int x, int y){
g[x][y] ^= 6;
for(int i = 0; i < 4; i ++){
g[x][i] ^= 6;
g[i][y] ^= 6;
}
}
int check(){
for(int i = 0; i < 4; i ++)
for(int j = 0; j < 4; j ++)
if(g[i][j] == '+') return 0;
return 1;
}
int main(){
for(int i = 0; i < 4; i ++) cin >> g[i];
vector<PII> path;
memcpy(backup, g, sizeof g);
for(int i = (1 << 16) - 1; i >= 0; i --){
for(int j = 0; j < 16; j ++){
if((i >> j & 1) == 0){
turn(j / 4, j % 4);
path.push_back({j / 4, j % 4});
}
}
if(check())
break;
memcpy(g, backup, sizeof g);
path.clear();
}
cout << path.size() << endl;
for(auto t : path) cout << t.first + 1 << ' ' << t.second + 1 << endl;
return 0;
}
位运算
优化成1维。
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int N = 10;
#define PII pair<int, int>
int g[N], backup[N];
void turn(int x, int y){
int d = 1 << (3 - y);
g[x] ^= d;
for(int i = 0; i < 4; i ++) g[i] ^= d;
g[x] ^= (1 << 4) - 1;
}
int check(){
for(int i = 0; i < 4; i ++)
if(g[i]) return 0;
return 1;
}
int main(){
for(int i = 0; i < 4; i ++){
for(int j = 0; j < 4; j ++){
char c = getchar();
g[i] = g[i] * 2 + (c == '+');
}
getchar();
}
vector<PII> path;
memcpy(backup, g, sizeof g);
for(int i = 0; i < 1 << 16; i ++){
for(int j = 0; j < 16; j ++){
if(i >> j & 1){
turn(j / 4, j % 4);
path.push_back({j / 4, j % 4});
}
}
if(check()) break;
path.clear();
memcpy(g, backup, sizeof g);
}
cout << path.size() << endl;
for(auto t : path) cout << t.first + 1 << ' ' << t.second + 1 << endl;
}