LOJ6037 猜数列

猜数列

有一个长度为\(m\)的,由\(1\)\(9\)之间的数构成的未知数列\(a\)

你现在有\(n\)个线索,每个线索都是用如下方式生成的:

  1. 选择序列\(a\)的某一个位置\(p\)作为开始。

  2. 选择某个方向(向左或向右)。

  3. \(p\)出发往你选择的方向走,每遇到一个之前未出现的数就将它加到线索中。

(可以发现,每条线索的长度都不超过\(9\)。)

现在你需要求出满足所有线索的长度最小的序列的长度。

对于\(100\%\)的数据,\(1\leq n \leq 10\)

题解

https://www.cnblogs.com/yyf0309/p/10124870.html

依次从左到右考虑每一位上填的数。

\(F[l][x][r][y][S]\)表示正在匹配往左走的线索\(l\)的第\(x\)位,正在匹配向右走的线索\(r\)的第\(y\)位,已经考虑过的线索集合是\(S\)的最小代价。

  • 往左走的线索已经匹配上了\(n\sim x+1\)。当\(x=0\)时说明已经完全匹配了。

  • 往右走的线索已经匹配上了\(1\sim y-1\)。当\(y=\text{len}_r+1\)时说明已经完全匹配了。

主要有两种转移:

  1. 填下一个字符

    如果两个线索正在匹配的字符相同,那么直接填。如果不同则还需判断一下是否会使得另一线索不满足条件。

    • 如果当前选择的字符没有匹配上往左走的下一个,那么往左走的线索需要满足\(x\sim 1\)中出现了这个字符。

    • 如果当前选择的字符没有匹配上往右走的下一个,那么往右走的线索需要满足\(1\sim y-1\)中出现了这个字符。

  2. 更换线索

    • 更换向左走的线索需要满足原线索的前缀能匹配新线索的后缀

      大概就是说当\(l=\{1,2,3,4,5\},x=3,l'=\{1,2,3\}\)的时候,可以由\(F[l][3][S]\)转移到\(F[l'][3][S]\)

      但是注意到这个形式,我们一定可以在\(x=0\)的时候更换向左走的线索。

    • 更换向右走的线索需要满足原线索的后缀能匹配新线索的前缀。

      大概就是说当\(r=\{1,2,3,4,5\},x=3,r'=\{3,4,5\}\)的时候,可以由\(F[r][3][S]\)转移到\(F[r'][1][S]\)

    能不能在某个串的某个位置处更换某个串可以预处理出来。

时间复杂度\(O(2^n\cdot n^3\cdot 10^2)\)

int n;
int a[11][12],len[11];
int b[11][12],go[11][11][11];

bool check(int x,int y,int z){ // x matching position y
	if(b[z][len[z]]&~b[x][len[x]]) return 0; // z has something x doesn't have
	for(int i=1;i<=len[z];++i)if(a[z][i]==a[x][y]) ++y;
	return y==len[x]+1;
}

int F[11][11][11][12][1<<10];

int solve(int l,int x,int r,int y,int mask){
	int&ans=F[l][x][r][y][mask&=bin(n)];
	if(ans!=-1) return ans;
	if(mask==bin(n) and x==0 and y==len[r]+1) return ans=0; // able to end
	ans=1e9;
	if(l<n and x==0){
		for(int i=0;i<=n;++i)if(~mask>>i&1)
			for(int j=1;j<=len[i]+1;++j)if(go[i][j][l])
				ans=min(ans,solve(i,j-1,r,y,mask|1<<i));
	}
	else{
		for(int i=0;i<n;++i)if(~mask>>i&1)
			if(go[r][y][i]) ans=min(ans,solve(l,x,i,1,mask|1<<i));
		for(int i=1;i<=9;++i){
			bool u=i==a[l][x],v=i==a[r][y];
			if(u and v) ans=min(ans,solve(l,x-1,r,y+1,mask)+1);
			if(u and b[r][y-1]>>i&1) ans=min(ans,solve(l,x-1,r,y,mask)+1);
			if(v and b[l][x]>>i&1) ans=min(ans,solve(l,x,r,y+1,mask)+1);
		}
	}
	return ans;
}

int main(){
	read(n);
	for(int i=0;i<n;++i){
		for(int x;read(x);) a[i][++len[i]]=x;
		for(int j=1;j<=len[i];++j) b[i][j]=b[i][j-1]|1<<a[i][j];
	}
	b[n][0]=~0; // n can concatenate everything
	for(int i=0;i<=n;++i)for(int j=1;j<=len[i]+1;++j)
		for(int k=0;k<=n;++k)if(k!=i)
			go[i][j][k]=check(i,j,k);
	memset(F,-1,sizeof F);
	int ans=1e9;
	for(int i=0;i<=n;++i)
		ans=min(ans,solve(i,len[i],n,1,1<<i));
	printf("%d\n",ans==1e9?-1:ans);
	return 0;
}

posted on 2020-04-20 21:21  autoint  阅读(192)  评论(0编辑  收藏  举报

导航