还没讲完 先记录一下目前想法
她将灯的亮暗视为灯的两种状态 记为1,0
例如 一串灯的亮暗情况为:
1010,此时我给出一种按灯的方案(1记为按下开关,0相反)
1100,那么最终的结果应该是(1010)^(1100)^(1100 >> 1)^(1100 << 1)
这是因为按下一个开关会对左中右三个点造成影响 而按两下开关相当于没有按开关 这一点和异或(^)很像 1100是对按下的点的影响
1100 << 1是对左边的点的影响
1100 >> 1是对右边的点的影响
因此四者异或和即为最后的开关灯状态
PS : 补充一下位运算的优先级 一般来说 位运算都要打括号
那么题目就很简单了 直接枚举第一行的操作 第二行的操作要使第一行满足条件 第三行要使第二行满足条件 以此类推 所有的种类就get了 最后只需判断最后一行是否满足条件
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll a[110], b[110], cur[110], change[110], n, m;
string s[110];
int cal(int x) {
int cnt = 0;
for (;x;x >>= 1) {
if(x & 1) cnt ++;
}
return cnt;
}
int deal(ll a[]) {
int cnt = 0, ans = 2e9;
for (change[0] = 0;change[0] <= (1 << m) - 1; ++ change[0]) {
cnt = cal(change[0]);
cur[0] = a[0] ^ change[0] ^ (change[0] >> 1) ^ ((change[0] << 1) & ((1 << m) - 1));
/*
此处注意 左移的值需要去&二进制下相同位数的(1...1) n个1
目的是为了避免这个多出来的1造成影响
第一次WA就是因为左移和右移哪个需要这个操作没搞懂
*/
cur[1] = a[1] ^ change[0];
for (int i = 1;i < n; ++ i) {
change[i] = cur[i - 1];
cnt += cal(change[i]);
cur[i] = cur[i] ^ change[i] ^ (change[i] >> 1) ^ ((change[i] << 1) & ((1 << m) - 1));
cur[i + 1] = a[i + 1] ^ change[i];
}
if (cur[n - 1] == 0) {
ans = min(cnt, ans);
}
}
return ans;
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 0;i < n; ++ i) cin >> s[i];
for (int i = 0;i < n; ++ i)
for (int j = 0;j < m; ++ j)
if (s[i][j] == 'b') a[i] += (1 << j);
else b[i] += (1 << j);
if(min(deal(a), deal(b)) > n * m) puts("Impossible");
else cout << min(deal(a), deal(b));
}
PS:这好像是更优解 一般解是搜索dfs 复杂度会更大