• 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
    ",ans>100?-1:ans);
    	return 0;
    }
    
  • 相关阅读:
    jQuery cxCalendar 日期选择器
    JS中this的4种绑定规则
    Redis入门教程
    大型网站限流算法的实现和改造
    JS中的继承(下)
    剑指Offer面试题:6.旋转数组中的最小数字
    剑指Offer面试题:5.重建二叉树
    剑指Offer面试题:4.从尾到头打印链表
    C++之运算符重载
    剑指Offer面试题:3.替换空格
  • 原文地址:https://www.cnblogs.com/zzqtxdy/p/12181283.html
Copyright © 2020-2023  润新知