这是道好题啊。
题意:二进制集合 s 要转变成二进制集合 e
每次可以对二进制集合中的某一位 i 进行翻转操作,问最少步数
首先将 n 个二进制数转成十进制数
要实现将 v 的第 i 个二进制位翻转,v ^= (1<<i)
( i 从右到左)
因为 0 的个数相同,1的个数也一定相同
剪枝1:s 与 e 的 0 的个数必须相同
方法一:求步数最少,一般是 bfs 或 id-dfs(明显上界是 L),这题 dfs 也行
方法二:也可以假设 s[0] 与 e[i] 相对应
记录要对应的话应该翻转哪些位置,枚举这些翻转
总复杂度 O(n^2)
要记录哪里不同:dif = s[0] ^ e[i]
,二进制位上为1的位置则不同
要对 v 进行翻转,只需执行 v ^ dif
#include <iostream>
#include <map>
#include <set>
#include <algorithm>
#include <string>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
int t, n, l;
const int maxn = 155;
int ans;
ll s[maxn], e[maxn], tmp[maxn];
int countbit(ll v)
{
int ret = 0;
while (v)
{
ret += (v&1);
v>>=1;
}
return ret;
}
bool solve()
{
int sv, ev;
bool same;
ans = 1000;
ll dif; int difcnt;
//s[0] 对应 e[j]
for (int j=0; j<n; j++)
{
//记录不一样的位置
dif = s[0] ^ e[j];
difcnt = countbit(dif);
for (int i = 0; i < n; ++i)
tmp[i] = (s[i] ^ dif);
sort(tmp, tmp+n);
same = true;
for (int i=0; i<n; i++) {
if (tmp[i] != e[i]){
same = false;
break;
}
}
if (same) ans = min(ans, difcnt);
}
return ans != 1000;
}
int main() {
freopen("A-small-practice.in", "r", stdin);
freopen("c.out", "w", stdout);
int kase = 1;
cin>>t;
while(cin>>n>>l){
getchar();
char d;
ll v;
for(int i=0; i<n; i++){
v = 0;
for (int j=0; j<l; j++){
scanf("%c", &d);
v <<= 1;
v |= d-'0';
}
s[i] = v;
getchar();
}
for(int i=0; i<n; i++){
v = 0;
for (int j=0; j<l; j++){
scanf("%c", &d);
v <<= 1;
v |= d-'0';
}
e[i] = v;
getchar();
}
printf("Case #%d: ", kase++);
sort(e, e+n);
if (solve())
printf("%d\n", ans);
else
puts("NOT POSSIBLE");
}
}