【题目】
题目描述:
一个芯片可以有 种不同的状态,不妨设为 到 。其中, 状态是准备状态。当芯片出现错误时,可能会处于任意状态。因此需要一个重置序列来将它变成准备状态。你的任务就是寻找这个重置序列。
当芯片处于状态 时接收了命令 ,它会立刻转变成状态 。对于任意初始状态,你找到的重置序列都应最终将它变成准备状态。在此基础上,你找到的重置序列应该最短。
输入格式:
第一行两个整数 ( , ),表示状态数和命令数。
接下来 行每行 个整数,表示状态 在接收到命令 时会变成状态 。注意,状态和命令都是从 开始编号的。其中, 是唯一一个准备状态。保证 。
输出格式:
输出一个序列表示最短操作序列。用 进制数来表示操作 。数码之间、行尾都不应有多余字符。如果有多组解,输出字典序最小的一组。如果无解,输出 。
样例数据:
输入
3 4
2 1 1 2
1 0 0 0
0 0 0 1
输出
101
提示:
【样例说明】
三种状态都会通过这个序列最终变成状态
:
0->1->1->0
1->0->2->0
2->0->2->0
【分析】
建议先仔细读一下题,搞清楚题目在说什么。
看到数据范围,很容易想到的是状态压缩之类的东西。
我们用 表示 这个位置上没有芯片, 表示有。
那么初始状态就是 (因为每个位置上都有芯片),目标状态是 (就是只有 这个位置上才有芯片)。
对于每种状态 ,考虑它可以转移到什么状态。
显然可以枚举每一种命令,根据 数组可以转换到状态 ,用 来更新 即可。
然后在转移过程中,记录一下前驱和转移到它的操作,转移到 时输出。
用 实现转移的过程。
【代码】
#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;
}