UVALive 4394 String painter

  题目大意:有两个字符串A,B,一次刷可以把A串一段刷成同一个字母,问至少要刷几次才能把A串变成B串。串长≤100。

  本来以为是个很简单的区间DP,后来发现直接区间DP是不行的,这玩意有后效性:刷完一整块之后这一块就变了。

  对于这种问题不如干脆利落一点,直接把 f[ ][ ]设成将空串(即不需要考虑A与B的相同)刷成B串的最小次数。

  这个时候的转移方程就是:

  for(int i=1;i<=n;++i)f[i][i]=1;
  for(int len=1;len<n;++len)
    for(int i=1;i+len<=n;++i){
      int j=i+len;f[i][j]=f[i+1][j]+1;
      for(int k=i+1;k<=j;++k)
        if(B[i]==B[k])
          f[i][j]=min(f[i][j],f[i+1][k]+f[k+1][j]);
    }

  这个转移方程是很巧妙的。

  首先赋值最坏情况,作为最大值,然后枚举k,进行更新。

  转移方程的思想是:如果在B串中,i和k是一样的,就可以划分区间进行更新。

  结论:在涂色的时候,区间的一个端点,一定可以作为第一个涂色。

  证明:区间涂色有两种方法:分成左右 / 先整个涂一遍再在里面涂。

  分成左右是子问题,先整个涂的话就可以先选择这个端点涂。

  所以在转移时,如果i和k是一样的,则可以有f[i][k]=f[i+1][k],只需要在涂k的时候把整个区间涂上就可以了。

  同样可以用上面的子问题思考方式证明。

  这样就把"空串变B串"解决了。但是我们是要把A串变B串,答案还需要统计一遍。

  设g[i]表示A串从1到i全部被涂成B的最小步数,用f来更新g。

  这个时候转移方程就是这样:

  g[1]=A[1]==B[1]?0:1;
  for(int i=2;i<=n;++i){
    if(A[i]==B[i]){g[i]=g[i-1];continue;}
    g[i]=f[1][i];
    for(int j=1;j<i;++j)
      g[i]=min(g[i],g[j]+f[j+1][i]);
  }

  这个转移也是比较有意思的,这里就不做分析了。

  对于这种显然只能用DP来做的、一般的转移又有后效性的题,不妨状态设大气一点,直接忽略后效性带来的影响,再变换方式统计答案。

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
#define FILE "4394"
using namespace std;

const int N = 110;
int n,f[N][N],g[N];
char A[N],B[N];

inline int gi(){
  int x=0,res=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
  while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void solve(){
  n=strlen(A+1);
  for(int i=1;i<=n;++i)f[i][i]=1;
  for(int len=1;len<n;++len)
    for(int i=1;i+len<=n;++i){
      int j=i+len;f[i][j]=f[i+1][j]+1;
      for(int k=i+1;k<=j;++k)
        if(B[i]==B[k])
          f[i][j]=min(f[i][j],f[i+1][k]+f[k+1][j]);
    }
  g[1]=A[1]==B[1]?0:1;
  for(int i=2;i<=n;++i){
    if(A[i]==B[i]){g[i]=g[i-1];continue;}
    g[i]=f[1][i];
    for(int j=1;j<i;++j)
      g[i]=min(g[i],g[j]+f[j+1][i]);
  }
  printf("%d\n",g[n]);
}

int main(){
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);
  while(~scanf("%s%s",A+1,B+1))solve();
  fclose(stdin);fclose(stdout);
  return 0;
}
String painter

 

posted @ 2017-10-15 15:18  Fenghr  阅读(310)  评论(0编辑  收藏  举报