开关灯
jzoj 3926
题目大意
有一个n×l的灯网,你可以把按某一列的灯的按钮,然后这一列开的关,关的开,你也可以把某一行的灯和别的行的灯调换,问从当前状态到目标状态最少按多少个按钮
输入样例
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列开关,得到00, 10, 11,然后依次交换后两行和前两行即可。
第二组测试数据,可以证明不可能达到要求的方案。
第三组测试数据,只需交换两行即可。
数据范围
40% 的数据:1⩽N,L⩽10.1 \leqslant N, L \leqslant 10.1⩽N,L⩽10.
100% 的数据:1⩽N⩽150,1⩽L⩽50,1⩽T⩽4.1 \leqslant N \leqslant 150,1 \leqslant L \leqslant 50,1 \leqslant T \leqslant 4.1⩽N⩽150,1⩽L⩽50,1⩽T⩽4.
解题思路
我们枚举当前状态的第一行和目标状态的第几行配对,然后我们可以用异或求出某一行是否按,然后我们看看剩下的行是否能找到可配对的行,如果都有
那这就是一组解,然后求最小值即可
代码
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll t, n, l, ans, g, pp, p[200], a[200], b[200];
string str;
ll js(ll x)//看有多少个1
{ll sum = 0;while(x) sum += x&1, x>>=1;return sum;
}
int main()
{scanf("%lld", &t);while(t--){memset(a, 0, sizeof(a));memset(b, 0, sizeof(b));scanf("%lld %lld", &n, &l);for (ll i = 1; i <= n; ++i){cin>>str;for (ll j = 1, g = 1; j <= l; ++j, g <<= 1)a[i] += (str[j - 1] - 48) * g;//转换为数字}for (ll i = 1; i <= n; ++i){cin>>str;for (ll j = 1, g = 1; j <= l; ++j, g <<= 1)b[i] += (str[j - 1] - 48) * g;}ans = 51;for (ll i = 1; i <= n; ++i){memset(p, 0, sizeof(p));g = a[1]^b[i];//那些要按p[i] = 1;//是否用过pp = 1;//是否找到配对的for (ll j = 2; j <= n; ++j){pp = 0;for (ll k = 1; k <= n && !pp; ++k)if (!p[k] && (a[j]^b[k]) == g)//未用过且可以配对p[k] = 1, pp = 1;if (!pp) break;}if (pp) ans = min(js(g), ans);//求最小值}if (ans == 51) printf("Impossible\n");else printf("%lld\n", ans);}return 0;
}