【题目描述】
一个芯片可以有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());
}