【状态压缩】【广度优先搜索】重置序

【题目描述】
一个芯片可以有N种不同的状态,不妨设为0到N-1。其中,0状态是准备状态。当芯片出现错误时,可能会处于任意状态。因此需要一个重置序列来将它变成准备状态。你的任务就是寻找这个重置序列。

当芯片处于状态i时接收了命令j,它会立刻转变成状态d[i,j]。对于任意初始状态,你找到的重置序列都应最终将它变成准备状态。在此基础上,你找到的重置序列应该最短。

【输入】
第一行两个整数n,m(2<=n<=8,1<=m<=16),表示状态数和命令数。

接下来n行每行m个整数,表示状态i在接收到命令j时会变成状态d[i,j]。注意,状态和命令都是从0开始编号的。其中,0是唯一一个准备状态。保证0<=d[i,j]<n。

【输出】
输出一个序列表示最短操作序列。用16进制数来表示操作(0-9,a-f)。数码之间、行尾都不应有多余字符。如果有多组解,输出字典序最小的一组。如果无解,输出-1。

【思路】

这道题大概意思就是构造一个操作序列,使任意状态下的芯片都能变为状态0。
考场上我并没有想到使用状态压缩,我使用的是迭代加深搜索。但是,如果不进行状态压缩,会重复处理一些等价状态,因为我们只关心当前所有芯片处理后的状态有哪些,而不关心每种初始状态下芯片处理后的状态分别是哪些。比如考虑一下两种情况:
1.初始状态为a的芯片当前状态为c,初始状态为b的状态为d。
2.初始状态为a的芯片当前状态为d,初始状态为b的状态为c。
这两种情况的后续处理其实是一样的,于是我们可以进行状态压缩,用一个int表示当前还有c、d两个状态,这样就可以免除很多冗杂的等价状态。
于是例如1001100中1就代表当前存在的状态,0就代表当前不存在的状态,再来进行广搜就行。
代码:

#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register
#define LL long long
using namespace std;
int n,m;
int dep=0;
string s="0123456789abcdef";
char ans[10001],len;
int a[8][16];
int tot=0;
int begin;
bool vis[(1<<20)-1];
char op[10000001];
int fa[10000001];
inline int get(int now,int opt)
{
	int ans=0;
	for(int re i=n-1;i>=0;i--)
	{
		if(now&(1<<i))
			ans|=1<<a[i][opt];
	}
	return ans;
}
void print(int u)
{
	if(fa[u])print(fa[u]);
	if(op[u]!='\0')printf("%c",op[u]);
}
int bfs()
{
	queue<int>q;
	q.push(begin);
	vis[begin]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();
		if(u==1)
		{
			print(u);
			exit(0);
		}
		for(int re i=0;i<m;i++)
		{
			int nxt=get(u,i);
			if(vis[nxt])continue;
			vis[nxt]=1;
			op[nxt]=s[i];
			fa[nxt]=u;
			q.push(nxt);
		}
	}
	return -1;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int re i=0;i<n;i++)
		for(int re j=0;j<m;j++)
			scanf("%d",&a[i][j]);
	begin=(1<<n)-1;
	printf("%d",bfs());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值