【2019/03/30测试T1】重置序

【题目】

传送门

题目描述:

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

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

输入格式:

第一行两个整数 n , m n,m 2 n 8 2\le n\le 8 1 m 16 1\le m\le 16 ),表示状态数和命令数。

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

输出格式:

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

样例数据:

输入
3 4
2 1 1 2
1 0 0 0
0 0 0 1

输出
101

提示:

【样例说明】

三种状态都会通过这个序列最终变成状态 0 0
0->1->1->0
1->0->2->0
2->0->2->0


【分析】

建议先仔细读一下题,搞清楚题目在说什么。

看到数据范围,很容易想到的是状态压缩之类的东西。

我们用 0 0 表示 i i 这个位置上没有芯片, 1 1 表示有。

那么初始状态就是 2 n 1 2^n-1 (因为每个位置上都有芯片),目标状态是 1 1 (就是只有 0 0 这个位置上才有芯片)。

对于每种状态 i i ,考虑它可以转移到什么状态。

显然可以枚举每一种命令,根据 d d 数组可以转换到状态 j j ,用 i i 来更新 j j 即可。

然后在转移过程中,记录一下前驱和转移到它的操作,转移到 1 1 时输出。

b f s bfs 实现转移的过程。


【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10,M=20,S=1<<8;
int n,m,status,d[N][M],fa[S],trans[S][S],vis[S];
queue<int>q;
void print(int x)
{
	if(fa[x]!=status-1)  print(fa[x]);
	int now=trans[fa[x]][x];
	putchar(now>=10?(now-10+'a'):(now+'0'));
}
bool bfs()
{
	int x,i,j;
	q.push(status-1),vis[status-1]=1;
	while(!q.empty())
	{
		x=q.front(),q.pop();
		if(x==1){print(1);return 1;}
		for(i=0;i<m;++i)
		{
			int to=0;
			for(j=0;(1<<j)<=x;++j)  if(x&(1<<j))  to|=(1<<d[j][i]);
			if(!vis[to])  q.push(to),fa[to]=x,trans[x][to]=i,vis[to]=1;
		}
	}
	return 0;
}
int main()
{
	int i,j;
	scanf("%d%d",&n,&m),status=1<<n;
	for(i=0;i<n;++i)for(j=0;j<m;++j)scanf("%d",&d[i][j]);
	if(!bfs())  puts("-1");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/88914435
t1