开关灯(jzoj 3926)

开关灯

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.
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.

解题思路

我们枚举当前状态的第一行和目标状态的第几行配对,然后我们可以用异或求出某一行是否按,然后我们看看剩下的行是否能找到可配对的行,如果都有
那这就是一组解,然后求最小值即可

代码

#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;
}
发布了334 篇原创文章 · 获赞 57 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ssllyf/article/details/104334352