折半枚举

刷NOIP模拟赛,想起多年以前学过的折半枚举以前学过的好多东西都忘了QAQ。。。

有这样一道题(取自黄学长博客):

【题目描述】

$n$点$m$双向边的图,每个点有$2$个状态:开和关。每次操作改变一个点的状态,以及与其有边直接相连的点的状态。问开启所有点至少需要多少次操作。

【输入格式】

第一行两个整数$n$,$m$。

第二行$n​$个整数,第$i​$个数表示第$i​$点的状态,$0​$为关,$1​$为开。

接下来$m$行,每行$2$个整数$a$,$b$,表示$a$和$b$直接相连,同一条边不会出现多次。

【输出格式】

第一行一个整数$k$表示最少的操作次数,所有数据保证至少有一组可行解。

第二行$k$个整数,表示操作的点的编号。

【样例输入】

4 3

1 1 0 0

2 3

1 3

2 4

【样例输出】

3

1 2 3

【数据范围】

对于$100%$的数据,1<=n<=40,0<=m<=500

$n$最大为$40$,显然这题的时间复杂度应该是指数级别的,但是直接枚举$2^{40}$会超时,怎么办?

首先预处理一下改变每个点会造成的影响。

由于直接枚举会超时。我们发现,可以先枚举前$\frac{n}{2}$个点是否进行操作,保存操作后的状态(用HASH表),然后再枚举后$\frac{n}{2}$个点是否进行操作,得出操作后的状态$s$,并$1^{n}-1$与$s$进行异或操作,得出$S_1$。

不难发现,如果前$\frac{n}{2}$个点进行了某种操作$s_2$使状态变成$S_1$,那么显然把操作$s_2$与操作$s$合并就可以使全部开关的状态变成$1$。

最后,只用在HASH表里找$S_1$是否存在就好了。别忘了判断是否为最优解。

因为每次只枚举一半,所以时间复杂度最多为$2^{20}$。

我不会告诉你我不贴代码是因为我死活调不出来QAQ


上面所用到的先枚举前一半的状态,保存结果,再枚举后一半与前一半进行某种操作得出结果的做法就是折半枚举。