zoukankan      html  css  js  c++  java
  • LOJ6037 猜数列

    猜数列

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

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

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

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

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

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

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

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

    题解

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

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

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

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

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

    主要有两种转移:

    1. 填下一个字符

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

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

      • 如果当前选择的字符没有匹配上往右走的下一个,那么往右走的线索需要满足(1sim 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^ncdot n^3cdot 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
    ",ans==1e9?-1:ans);
    	return 0;
    }
    
  • 相关阅读:
    在线图片压缩
    wiki-editor语法
    Android 4.0.4模拟器安装完全教程(图文)
    Javascript中的void
    守护进程
    jQuery编程的最佳实践
    JavaScript内存优化
    vim编程技巧
    MySQL表的四种分区类型
    SQL中的where条件,在数据库中提取与应用浅析
  • 原文地址:https://www.cnblogs.com/autoint/p/12740628.html
Copyright © 2011-2022 走看看