LOJ#6037「雅礼集训 2017 Day4」猜数列

题目大意

给出一些线索,表示从一个位置选一个方向(左或右)走到尽头,每个数字的出现顺序。
求满足条件的最短序列的长度,数字和线索数都在\(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\n",ans>100?-1:ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzqtxdy/p/12181283.html