开 关 灯 开关灯 开关灯
题目链接: j z o j 3926 jzoj\ 3926 jzoj 3926
题目
在一个庆典上,
n
∗
L
n * L
n∗L盏灯摆成了一个
n
n
n行
L
L
L列的长方形。起初,有些灯是亮的,有些灯是灭的。每列的灯是串联在一起的。一共有
L
L
L个开关,按下第
i
i
i个开关可以改变第
i
i
i列所有灯的状态,即这一列亮的灯熄灭,熄灭的灯点亮。而且,不同的行是可以随意交换的。
主办方希望所有灯以亮或暗的不同状态组成一个图案。请你计算能否通过控制开关形成这个图案。如果可以,请计算最少按多少次开关。
输入
第一行有一个整数
T
T
T,表示有多少组测试数据。
每组测试数据包含三行。第一行为两个整数
n
n
n ,
L
L
L。
每组数据的第二行为
n
n
n个长度为
L
L
L的
0
/
1
0/1
0/1字符串,依次描述起初每行的灯的开关状态。第
i
i
i个字符串的第
j
j
j个字符若是
’
1
’
’1’
’1’,表示对应位置的灯是亮的;
’
0
’
’0’
’0’表示是灭的。
每组数据的第三行为
n
n
n个长度为
L
L
L的
0
/
1
0/1
0/1字符串,描述主办方希望达到的所有灯的开关状态。格式同上。
输出
输出 T T T行,依次为每组测试数据的答案。如果不可能达到,输出 ” I m p o s s i b l e ” ”Impossible” ”Impossible”;否则输出最少按多少次开关。
样例输入
3
3 2
01 11 10
11 00 10
2 3
101 111
010 001
2 2
01 10
10 01
样例输出
1
Impossible
0
样例解释
第一组测试数据,按第
2
2
2列开关,得到
00
00
00 ,
10
10
10 ,
11
11
11,然后依次交换后两行和前两行即可。
第二组测试数据,可以证明不可能达到要求的方案。
第三组测试数据,只需交换两行即可。
数据范围
40
%
40\%
40% 的数据:
1
<
=
N
,
L
<
=
10.
1\! <=\! N, L\! <=\! 10.
1<=N,L<=10.
100
%
100\%
100% 的数据:
1
<
=
N
<
=
150
1\! <=\! N\! <=\! 150
1<=N<=150,
1
<
=
L
<
=
50
1\! <=\! L\! <=\! 50
1<=L<=50,
1
<
=
T
<
=
4.
1\! <=\! T\! <=\! 4.
1<=T<=4.
思路
这道题要用二进制来做。
我们可以先求出每一行的
1
/
0
1/0
1/0数组换成十进制是多少(用
l
o
n
g
l
o
n
g
long\ long
long long来存),然后我们先枚举原来数组的第一行用最后对应第几行,然后异或出它们的值。这个值就是有哪些不同的地方,即若这个数在二进制下的第
i
i
i位是
1
1
1则表示第
i
i
i列要按。
那我们就对于每一次枚举,就再枚举剩下的数,将它们配对。若不能配对,则这样按不行。若能配对,则找出能配对中所要按次数最少的那一次,就是答案了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
ll T, n, l, a[151], b[151], ans;
bool in[151];
char c;
ll getnum(ll x) {//得到二进制
if (!x) return 1;
ll sum = getnum(x / 2);
if (x % 2 == 0) return sum * sum;
return sum * sum * 2;
}
ll getans(ll x) {//得到要按的个数
ll sum = 0;
while (x) {
if (x % 2 == 1) sum++;
x /= 2;
}
return sum;
}
int main() {
scanf("%d", &T);//读入
for (int t = 1; t <= T; t++) {
memset(a, 0, sizeof(a));//初始化
memset(b, 0, sizeof(b));
ans = 2147483647;
scanf("%lld %lld", &n, &l);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= l; j++) {
c = getchar();
while (c != '1' && c != '0') c = getchar();
if (c == '1') a[i] += getnum(l - j);//用二进制存
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= l; j++) {
c = getchar();
while (c != '1' && c != '0') c = getchar();
if (c == '1') b[i] += getnum(l - j);//这个也是用二进制存
}
for (int i = 1; i <= n; i++) {//找和谁匹配
bool yes = 0;
ll dif = a[1] ^ b[i];//异或
memset(in, 0, sizeof(in));
in[i] = 1;
for (int j = 2; j <= n; j++) {
yes = 1;
for (int k = 1; k <= n; k++)
if (!in[k] && (ll)(a[j] ^ b[k]) == dif) {//配对
yes = 0;
in[k] = 1;
break;
}
if (yes) break;//没得配对
}
if (yes) continue;
ans = min(ans, getans(dif));//求出最少要按多少次
}
if (ans == 2147483647) printf("Impossible\n");//一定按不出来
else printf("%lld\n", ans);//输出
}
return 0;
}