[状压DP]关灯问题II
关 灯 问 题 I I 关灯问题II 关灯问题II
题目描述
现有n盏灯,以及 m m m个按钮。每个按钮可以同时控制这 n n n盏灯——按下了第 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
样例输入
3
2
1 0 1
-1 1 0
样例输出
2
题目解析
先讲一下题目大意,有
n
n
n盏电灯,
m
m
m个开关。
m
m
m个开关对每盏电灯都有一个不同的效果;
例如,有
3
3
3盏电灯,
2
2
2个开关,那么每个开关都有
3
3
3个值,分别是对第
1
1
1个开关,第
2
2
2个开关,第
3
3
3个开关的值。这个值有
3
3
3种类型:如果为
1
1
1,那么当这盏灯开了的时候,把它关上,否则不管;如果为
−
1
-1
−1的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是
0
0
0,无论这灯是否开,都不管。
因为 0 0 0的状态是不会改变电灯的状态的,所以只有 2 2 2种状态是有用的。
---------------------------------------------------------分割线------------------------------------------------------------------
然后,我们用状压DP来做这一题。我们用一个二进制数来表示当前电灯的状态,并转为十进制储存到数组里。而修改状态就使用位运算来处理。
code
#include<cmath>
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n, m, et=1;
int dp[1 << 10], a[105][15];
int main ()
{
scanf ("%d%d",&n,&m);
for (int i = 1; i <= m; ++i)
{
for (int j = 1; j <= n; ++j)
{
scanf("%d",&a[i][j]);
}
}
memset(dp,0x3f,sizeof(dp));
dp[(1 << n) - 1]=0;
for (int i = (1 << n) - 1; i >= 0; --i)
{
for (int j=1; j <= m; ++j)
{
et=i;
for (int k=1; k <= n; ++k)
{
if (!a[j][k]) continue;
else if (a[j][k] == 1 && (i & (1 << k - 1)))
et ^= (1 << (k - 1));
else if (a[j][k] == -1 && !(i & (1 << k - 1)))
et ^= (1 << k - 1);
}
dp[et] = min (dp[et], dp[i] + 1);
}
}
printf ("%d", dp[0] == 0x3f3f3f3f ? -1 : dp[0]);
return 0;
}