题目描述
现有 n n n 盏灯,以及 m m m 个按钮。每个按钮可以同时控制这 n n n 盏灯——按下了第 i i i 个按钮,对于所有的灯都有一个效果。按下 i i i 按钮对于第 j j j 盏灯,是下面 3 3 3 中效果之一:如果 a i , j a_{i,j} a i , j 为 1 1 1 ,那么当这盏灯开了的时候,把它关上,否则不管;如果为 − 1 -1 − 1 的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是 0 0 0 ,无论这灯是否开,都不管。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。
输入格式
前两行两个数,n , m n, m n , m 。
接下来 m m m 行,每行 n n n 个数 , a i , j ,a_{i, j} , a i , j 表示第 i i i 个开关对第 j j j 个灯的效果。
输出格式
一个整数,表示最少按按钮次数。如果没有任何办法使其全部关闭,输出 − 1 -1 − 1 。
思路:状态压缩bfs
因为 n ≤ 10 n \leq 10 n ≤ 1 0 ,所以可以用一个数字表示灯的开关状态。
基本定义
设 s t a t e state s t a t e 表示当前灯的开关状态。
如果第 i i i 个灯开启,那么 s t a t e state s t a t e (二进制)中的第 i i i 位为 1 1 1 (以最右边为第 0 0 0 位)。
使用 v i s [ s t a t e ] vis[state] v i s [ s t a t e ] 记录 s t a t e state s t a t e 状态是否访问过。
按钮效果处理
1 : x → x a n d 0 1: x \rightarrow x \mathrm{and} 0 1 : x → x a n d 0
0 : x → x 0: x \rightarrow x 0 : x → x
− 1 : x → x o r 1 -1: x \rightarrow x \mathrm{or} 1 − 1 : x → x o r 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <bits/stdc++.h> using namespace std;using ll = long long ;inline void minset (int &t, int other) { t = min (t, other); } inline void maxset (int &t, int other) { t = max (t, other); } const int inf = 0x7f7f7f7f ;const int N = (1 << 10 ) + 10 ;const int M = 110 ;int step[N];bool vis[N];int op[M][11 ];int n, m;int bfs () { int full = (1 << n) - 1 ; queue<int > q; q.push (full); vis[full] = true ; while (q.size ()) { int from = q.front (); q.pop (); for (int i = 1 ; i <= m; i++) { int state = from; for (int j = n - 1 ; j >= 0 ; j--) { int op_num = op[i][j]; switch (op_num) { case -1 : state |= (1 << j); break ; case 1 : state &= (full ^ (1 << j)); } } if (!vis[state]) { q.push (state); vis[state] = true ; step[state] = step[from] + 1 ; } if (!state) { return step[state]; } } } return -1 ; } int main () { cin >> n >> m; for (int i = 1 ; i <= m; i++) { for (int j = n - 1 ; j >= 0 ; j--) { cin >> op[i][j]; } } cout << bfs () << endl; return 0 ; }
补充
既然是状态压缩,为什么不用状压dp?
考虑时间复杂度:
有 n n n 个灯泡,状态数为 2 n 2^n 2 n 。
有 m m m 种操作,因为灯泡的亮暗与操作顺序有关 ,所以需要枚举所有状态的全排列 ,约 m ! m! m ! 种。
将它们相乘,时间复杂度为 O ( 2 n ⋅ m ! ) O(2^n \cdot m!) O ( 2 n ⋅ m ! ) 。
所以,时间会炸,要用一些优化才行,但是这样反而比bfs复杂了。