luogu2622关灯问题题解--状压DP

题目链接

https://www.luogu.org/problemnew/show/P2622

分析

这题有点不一样,每个开关可以重复使用那么用每个开关作为阶段似乎不太好,那么我们按照题意设$f[sta]$为达到$sta$这个状态最少操作次数,我们枚举到达下一个状态用了哪个开关,假设用了某个开关后的状态为$x$,显然$f[x]=min(f[x],f[sta]+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
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
#define ll long long
#define ri register int
using std::min;
using std::max;
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=getchar()))ne=c=='-';
x=c-48;
while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=10005;
const int inf=0x7fffffff;
int n,m;
int f[1<<10+1];
int op[103][10];
int main(){
int x,y,sz,sta;
read(n),read(m);//n 灯 m 开关
for(ri i=1;i<=m;i++){
for(ri j=1;j<=n;j++){
read(op[i][j]);
}
}
sz=1<<n;
memset(f,0x3f,sizeof(f));
f[sz-1]=0;
for(ri k=sz-1;k>=0;k--){
for(ri i=1;i<=m;i++){
sta=k;
for(ri j=0;j<n;j++){
if(op[i][j+1]==1&&(k&(1<<j)))sta=sta^(1<<j);
else if(op[i][j+1]==-1&&(!(k&(1<<j))))sta=sta|(1<<j);
}
f[sta]=min(f[sta],f[k]+1);
}
}
if(f[0]==f[sz])puts("-1");
else printf("%d\n",f[0]);
return 0;
}