题目大意
给出一些线索,表示从一个位置选一个方向(左或右)走到尽头,每个数字的出现顺序。
求满足条件的最短序列的长度,数字和线索数都在(10)之内。
题解
只考虑从左往右走,形成了一堆后缀。
按左端点从小到大排序,相当于构造序列,加数直到某个时刻切换到下一个。
比如({ 1,2,3,4,5})走了两个后可以切换到({ 3,2,4,1,5}),因为后一个序列数字出现的顺序跟前一个的后(3)个是一样的。
注意({ 1,2,3,4,5})不能切换到({ 2,3,4,5,6}),因为(6)没有出现过。
这样我们就得到了一个(dp)。(f_{r,y,s})表示已经考虑了集合(s),当前在构造第(r)个线索,前(y)位都构造出来了的最小序列长度。
两种转移:
1.切换到第(i)个线索,要求(i)不在(s)中,第(r)个的第(y)位以后的那一段可以用第(i)个线索代替,转移到(f_{i,0,s|(1<<i)})
2.加一个数,要求第(r)个还没构造完。转移到(f_{r,y+1,s}),序列长度(+1)。
考虑两边都有,可以用(f_{l,x,r,y,s})表示从左往右正在构造第(r)个,放了(y)个数;往左构造(r),放了从(x)往后的那些数。
(l)这里的切换可以逆着考虑,也是可以转移的(直接转移是错的,因为一个数第一次出现是在最右边)
1.(r)切换到第(i)个线索,要求(i)不在(s)中,第(r)个的第(y)位以后的那一段可以用第(i)个线索代替,转移到(f_{l,x,i,0,s|(1<<i)})
2.(l)放完了,切换到第(i)个线索的第(j)位,要求(i)不在(s)中,第(i)个的第(j)位以后的那一段可以用第(l)个线索代替,转移到(f_{i,j,r,y,s|(1<<i)})
3.加一个数,枚举加的是哪个,看下跟(l)的下一个及(r)的下一个是否相等,并且要求没有跟(l)和(r)冲突(即在前(x)个中已经出现过)。
一个小细节:(l)放完后不能加数,因为不知道会不会跟下一个冲突,但是如果之后不再从右往左构造则可以加数,可以用(l=n)表示接下来不再构造(l),详见代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int rd(){
int x=0,flg=1;
char c=getchar();
for (;(c<48||c>57)&&c!='-';c=getchar());
if (c=='-') flg=-1,c=getchar();
for (;c>47&&c<58;x=x*10+c-48,c=getchar());
return flg*x;
}
int n,m,len[11],a[11][11],b[11][11],g[11][11][11],f[11][11][11][11][1024];
bool check(int i,int j,int x){
for (int k=0;k<len[x];++k)
if (j<len[i]&&b[i][a[x][k]]==j) ++j;
else if (b[i][a[x][k]]>j) return 0;
return j==len[i];
}
int dfs(int l,int x,int r,int y,int s){
if (!x&&y==len[r]&&s==(1<<n)-1) return 0;
if (f[l][x][r][y][s]!=-1) return f[l][x][r][y][s];
int ret=0x3f3f3f3f;
if (l<n&&!x){
for (int i=0;i<n;++i)
if (!(s>>i&1))
for (int j=0;j<len[i];++j)
if (g[i][j][l]) ret=min(ret,dfs(i,j,r,y,s|1<<i));
ret=min(ret,dfs(n,0,r,y,s));
}
for (int i=0;i<n;++i)
if (!(s>>i&1)&&g[r][y][i])
ret=min(ret,dfs(l,x,i,0,s|1<<i));
for (int i=1;i<10;++i){
int fl=x&&a[l][x-1]==i,fr=y<len[r]&&a[r][y]==i;
if (fl&&fr) ret=min(ret,dfs(l,x-1,r,y+1,s)+1);
if (fl&&b[r][i]<=y) ret=min(ret,dfs(l,x-1,r,y,s)+1);
if (fr&&b[l][i]<=x) ret=min(ret,dfs(l,x,r,y+1,s)+1);
}
f[l][x][r][y][s]=ret;
return ret;
}
int main()
{
n=rd();
memset(b,0x3f,sizeof(b));
for (int i=0,j,x;i<n;len[i]=j,++i)
for (j=0,x=rd();x;++j,x=rd()) a[i][j]=x,b[i][x]=j;
for (int i=1;i<10;++i) b[n][i]=0;
for (int i=0;i<n;++i)
for (int j=0;j<len[i];++j)
for (int x=0;x<n;++x) g[i][j][x]=check(i,j,x);
for (int i=0;i<n;++i)
g[i][0][n]=1,g[n][0][i]=1;
memset(f,-1,sizeof(f));
int ans=0x3f3f3f3f;
for (int i=0;i<n;++i)
ans=min(ans,dfs(i,len[i],n,0,1<<i));
printf("%d
",ans>100?-1:ans);
return 0;
}