洛谷P2622关灯问题-状态压缩广度优先搜索
具体解法是:对队首的某一状态,枚举每一个开关灯操作,记录到达这一新状态的步数(也就是老状态 + 1),若是最终答案,输出,若不是,压入队列。采用结构体记录每个节点,包含当前状态和步数。
#include<bits/stdc++.h>
using namespace std;
typedef struct{
int status,cnt;
}NODE;
int vis[100020]={0};
int arr[200][200]={0};
int n,m;int num=0,min_step=9999;
queue<NODE> steps;
void bfs(int level)
{
if(steps.empty())return;
NODE pre_node=steps.front();
steps.pop();
if(pre_node.status==0)
{
cout<<pre_node.cnt;
min_step=min(min_step,pre_node.cnt);
return;
}
for(int i=1;i<=m;i++)
{//枚举所有状态
NODE new_node=pre_node;
int new_status=new_node.status;
for(int j=1;j<=n;j++)
{
if(arr[i][j]==1)
{
if(new_status&(1<<(n-j)))//说明是1
new_status=new_status^(1<<(n-j));
}
if(arr[i][j]==-1)
{
new_status=new_status|(1<<(n-j));
}
}
if(!vis[new_status])
{
new_node.status=new_status;
new_node.cnt=level;
steps.push(new_node);
vis[new_status]=1;
}
}
bfs(steps.front().cnt+1);
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
num=(num<<1)+1;
}
NODE node={num,0};
steps.push(node);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
cin>>arr[i][j];
}
vis[num]=1;
bfs(1);
if(min_step==9999) cout<<-1<<endl;
return 0;
}
较为简便的递推dp代码:
#include<bits/stdc++.h>
using namespace std;
vector<int>dp(1025,-1);int arr[200][200];
int n,m;
int main()
{
queue<int>q;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
cin>>arr[i][j];
}
q.push((1<<n)-1);dp[(1<<n)-1]=0;
while(!q.empty())
{
int c_state=q.front();
q.pop();
for(int i=1;i<=m;i++)
{
int n_state=c_state;
for(int j=1;j<=n;j++)
{
if(arr[i][j]==1)
if(n_state&(1<<(n-j)))
n_state=n_state^(1<<(n-j));
if(arr[i][j]==-1)
n_state=n_state|(1<<(n-j));
}
//cout<<dp[n_state]<<endl;
if(dp[n_state]==-1)
{
dp[n_state]=dp[c_state]+1;
q.push(n_state);
}
}
}
cout<<dp[0]<<endl;
return 0;
}