P2622 关灯问题II

现有 \(n\) 盏灯,以及 \(m\) 个按钮。每个按钮可以同时控制这 \(n\) 盏灯——按下了第 \(i\) 个按钮,对于所有的灯都有一个效果。按下 \(i\) 按钮对于第 \(j\) 盏灯,是下面3中效果之一:如果 \(a_{ij}\)\(1\),那么当这盏灯开了的时候,把它关上,否则不管;如果为 \(-1\) 的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是 $04 ,无论这灯是否开,都不管。

现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。

对于100%数据 n<=10,m<=100.


Link

状压dp 水题,因为难的题自己也不会做QAQ。

\(f[i]\) 表示 达到 \(i\) 这个状态所需要的最少的操作次数。

转移的时候枚举一下按哪一个按钮,在模拟一下得到最终的状态,然后就可以直接 \(dp\) 了。

上限大概是很松的,复杂度为 \(O(2^nnm)\)

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,a[110][10],f[(1<<12)];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= m; i++)
	{
		for(int j = 1; j <= n; j++)
		{
			a[i][j] = read();
		}
	}
	for(int i = 0; i < (1<<n)-1; i++) f[i] = 2333333;
	f[(1<<n)-1] = 0;
	for(int i = (1<<n)-1; i >= 0; i--)
	{
		for(int j = 1; j <= m; j++)
		{
			int tmp = i;
			for(int k = 1; k <= n; k++)
			{
				if(a[j][k] == 0) continue;
				if(a[j][k] == 1)
				{
					if(i & (1<<(k-1))) tmp = tmp & (~(1<<(k-1))); //把tmp的第k-1位赋零
				}
				if(a[j][k] == -1)
				{
					if(!(i & (1<<(k-1)))) tmp = tmp | (1<<(k-1));//把tmp的k-1位赋一
				}
			}
//			cout<<tmp<<endl;
			f[tmp] = min(f[tmp],f[i] + 1);//tmp就是按下第j个按钮变成的状态
		}
	}
	if(f[0] == 2333333) printf("%d\n",-1);
	else printf("%d\n",f[0]);
	return 0;
}
posted @ 2020-10-19 11:47  genshy  阅读(101)  评论(0编辑  收藏  举报