UVa 1625 color length--dp状态转移的巧妙计算

题目链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4500
分析
这道题的规划方向是很容易找到的。如果用 d[i][j] 表示第一串规划到第 i 个字符,第二串规划到第 j 个字符的最优“代价”,而由于 d[i][j] 由状态 d[i1][j] d[i][j1] 转移而来,所以只要求的状态转移的花费就能A掉这道题了。即

d[i][j]=min{d[i1][j]+c[i1][j],d[i][j1]+c[i][j1]}

然而如果用常规的思想 d[i][j] 表示当前状态下的最优值的话,计算将变得非常困难。所以可以采用《算法入门经典》中的想法, d[i][j] 表示成最优代价与还未结束字符的总代价。这样就可以先处理出 d[i][j] 中还未结束的字符数目 c[i][j] 。我们可以预处理出每个字符在字符串中的开始和结束的位置。这样在下一次规划的时候就可以在 O(1) 的时间完成状态转移。
状态转移方程

d[i][j]=min{d[i1][j]+c[i1][j],d[i][j1]+c[i][j1]}

d[la][lb] 就是所求答案。
代码

#include
#include
#include
#define INF 0x3f3f3f3f
#define maxn 5010

using namespace std;
char a[maxn],b[maxn];
int d[maxn][maxn],c[maxn][maxn];
int sa[26],ea[26],sb[26],eb[26];



int main()
{
    //freopen("H:\\c++\\file\\stdin.txt","r",stdin);
    int T;
    scanf("%d",&T);

    while(T--)
    {
        scanf("%s%s",a+1,b+1);
        int la = strlen(a+1);
        int lb = strlen(b+1);
        for(int i=1 ; i<=la ; ++i)a[i]-='A';
        for(int i=1 ; i<=lb ; ++i) b[i] -='A';
        for(int i=0 ; i<26 ; ++i){
            sa[i] = sb[i] = INF;
            ea[i] = eb[i] = -INF;
        }

        for(int i=1 ; i<=lb ; ++i){
            sb[b[i]] = min(sb[b[i]],i);
            eb[b[i]] = i;
        }
        for(int i=1 ; i<=la; ++i){
            sa[a[i]] = min(sa[a[i]],i);
            ea[a[i]] = i;
        }



        d[0][0] =c[0][0] = 0;
        for(int i=0 ; i<=la ; i++)
        {
            for(int j=0 ; j<=lb ; ++j)
            {

                if(!i && !j)continue;
                //d
                int v1 = INF;int v2 = INF;
                if(i)  v1 = d[i-1][j] + c[i-1][j];
                if(j)  v2 = d[i][j-1] + c[i][j-1] ;
                d[i][j] = min(v1,v2);


                //c
                if(i)
                {
                    c[i][j] = c[i-1][j];
                    if(sa[a[i]] == i && sb[a[i]] >j)c[i][j]++ ;
                    if(ea[a[i]] == i &&eb[a[i]]<=j) c[i][j] --;
                }else if(j)
                {
                    c[i][j] = c[i][j-1];
                    if(sb[b[j]] == j &&sa[b[j]]>i)c[i][j] ++;
                    if(eb[b[j]] == j&&ea[b[j]] <=i)c[i][j]--;
                }

            }

        }
        printf("%d\n",d[la][lb]);
     } 

    return 0;
}

值得注意的是可以用滚动数组优化存储空间

你可能感兴趣的:(算法刷题)