[DP 压位] BZOJ 2915 [Poi1997] gen Genotypes

来源:互联网 发布:网络语膜拜是什么意思 编辑:程序博客网 时间:2024/05/17 02:24

Tn3复杂度能过?


搬题解


逆向思维. 设d[i,j,c]为真当且仅当[i,j)的子串可以合成字母c. 递推时可枚举分裂位置k: 对于某个规则A1A2A3, 如果d[i,k,A2]与d[k,j,A3]均为真, 则d[i,j,A1]为真. 即:

d[i,j,A1] = d[i,j,A1]OR (A[i,k,A2] AND A[k,j,A3])

状态有cn2个, 转移有c2n个, 总O(c3n3)。

    g[i,j]为[I,j)的子串最少可以合成几个S,直接动态规划即可, 时间复杂度为O(n2).

优化:最多只有26个字母, 因此可以用位运算加速

e[A2,A3]表示可以分裂得到A2A3的字符集

d’[I,j]表示所有c对应的d[i,j,c]所组成的二进制数

    时间复杂度降为O(c2n3).


#include<cstdio>#include<cstdlib>#include<algorithm>using namespace std;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }  return *p1++;}inline void read(int &x){  char c=nc(),b=1;  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;}inline int read(char *s){  char c=nc(); int len=0;  for (;!(c>='A' && c<='Z');c=nc());  for (;c>='A' && c<='Z';s[++len]=c,c=nc()); s[++len]=0; return len-1;}const int N=105;int n,m;int f[26][26];int g[N][N]; char s[N];int F[N];inline int calc(int a,int b){  int ret=0;  for (int x=a,t=__builtin_ctz(x&-x);x;x-=x&-x,t=__builtin_ctz(x&-x))    for (int y=b,s=__builtin_ctz(y&-y);y;y-=y&-y,s=__builtin_ctz(y&-y))      ret|=f[t][s];  return ret;}int main(){  char a[5]; int Q;  freopen("t.in","r",stdin);  freopen("t.out","w",stdout);  read(m);  for (int i=1;i<=m;i++)    read(a),f[a[2]-'A'][a[3]-'A']|=1<<(a[1]-'A');  read(Q);  while (Q--){    n=read(s);    for (int i=1;i<=n;i++){      g[i][i]=1<<(s[i]-'A');      for (int j=i-1;j;j--){g[j][i]=0;for (int k=j;k<i;k++)  g[j][i]|=calc(g[j][k],g[k+1][i]);      }    }    F[0]=0;    for (int i=1;i<=n;i++){      F[i]=1<<30;      for (int j=1;j<=i;j++)if (g[j][i]&(1<<18))  F[i]=min(F[i],F[j-1]+1);    }    if (F[n]!=(1<<30))      printf("%d\n",F[n]);    else      printf("NIE\n");  }  return 0;}


0 0
原创粉丝点击