[状压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;
}

posted @ 2020-08-21 08:08  unknown_future  阅读(59)  评论(0编辑  收藏  举报