题目大意:
题目链接:https://www.luogu.org/problemnew/show/P2622
有个开关和盏灯,第个开关要么可以开启第盏灯,要么可以关上第盏灯,要么不对第盏灯起作用。求把盏灯全部打开的最少步数。
思路:
这道题很明显可以用做。因为对于每一种情况,我们也就只有种转移方法,而求的是最优解。
而最多只有10盏灯,所以可以想到用状压。这样每种情况就被压缩成了~中的一个数。
那么搜就好了。
注意细节。
代码:
#include <cstdio>
#include <queue>
using namespace std;
int n,m,a[101][11];
bool p[1024];
int change(int x,int y) //转换
{
for (int i=1;i<=n;i++)
{
if (a[y][i]==1) x|=(1<<(n-i));
//如果这个开关可以打开这盏灯,那么就直接或,因为1 or 1=0 or 1=1
if (a[y][i]==-1&&(x&(1<<(n-i)))) x^=(1<<(n-i));
//如果这个开关可以关上这盏灯,那么就要判断这一位是不是1,如果是1才异或。
}
return x;
}
void bfs()
{
queue<int> dis; //最小步数
queue<int> q; //状态(已压缩)
q.push(0);
dis.push(0);
while (q.size())
{
int u=q.front();
int d=dis.front();
q.pop();
dis.pop();
for (int i=1;i<=m;i++) //美剧每一个开关
{
int v=change(u,i);
if (p[v]) continue; //判重
p[v]=1;
q.push(v);
dis.push(d+1);
if (v==(1<<n)-1) //全部打开
{
printf("%d\n",d+1);
return;
}
}
}
printf("-1");
return;
}
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]);
bfs();
return 0;
}