BZOJ2121-字符串游戏

题解:

有难度的dp

感觉比较容易想到的dp是f[i][j]表示能否完全删除

转移时枚举i',j'转移,这样用hash可以做到l^4

但是 这是错的

因为我们可以将序列分为三段或者更多段相加

所以刚才的定义是有问题的

既然可以多段我们显然不能暴力枚举,那么我们就记录匹配了多少

f[i][j][k][l]表示i-j这段区间,能否匹配到k的l处

然后问题在于转移

第一种直接多匹配一位是很简单的

第二种在于要匹配一堆字符

我们能否直接枚举一个串来解决的

很明显示不能的,因为同刚才说的可能是多个搞在了一起形成的

所以我们还需要一个状态p[i][j]表示i-j能否被完全删除

时间复杂度l^3*n*m

数据不是很强加上有很多冗余状态于是就过了

网上代码有一些卡常数的东西。。

1.区间dp压掉一维,将区间dp变为从右向左枚举左端点,从左向右枚举右端点

2.最后一维状压,注意转移那里如果状压是可以做到(n+m)的

复杂度l^3*(n+m)

 

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
char c[200],s[200][200];
int m;
bool f[152][152][31][21],g[152][152];
int t[152];
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);  
  cin>>c;
  cin>>m;
  rep(i,1,m) cin>>s[i];
  int n=strlen(c);
  dep(i,n,1) c[i]=c[i-1];
  rep(i,0,n)
    rep(j,0,n)
      rep(k,1,m)
        if (i>j)
        f[i][j][k][0]=1;
  rep(i,1,n)
    rep(j,1,n-i+1)
    {
      rint l=j,r=j+i-1;
      rep(k,1,m)
      {
        bool* a=f[l][r][k],*b=f[l][r-1][k];
        int len=strlen(s[k]);
        rep(i1,1,len)
          if (c[r]==s[k][i1-1]&&b[i1-1])
            a[i1]=1;
        rep(i1,1,len)
          rep(i2,l+1,r)
            if (g[i2][r]&&f[l][i2-1][k][i1])
              a[i1]=1;
        if (a[len]) g[l][r]=1;
      }
    }
  rep(i,1,n)
  {
    rep(j,1,i)
    {
      t[i]=max(t[i],t[j-1]+((g[j][i]==1)?(i-j+1):0));
    }
    t[i]=max(t[i-1],t[i]);
  }
  cout<<n-t[n]<<endl;
  return 0; 
}

 

posted @ 2018-09-10 23:34  尹吴潇  阅读(146)  评论(0编辑  收藏  举报