• hdu 1513 && 1159 poj Palindrome (dp, 滚动数组, LCS)


    题目

    以前做过的一道题, 今天又加了一种方法 整理了一下。。。。。

    题意:给出一个字符串,问要将这个字符串变成回文串要添加最少几个字符。

    方法一:

    将该字符串与其反转求一次LCS,然后所求就是n减去 最长公共子串的长度。

    额,,这个思路还是不是很好想。

    LCS:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 const int maxn = 5000+10;
     6 char s1[maxn], s2[maxn];
     7 int d[2][maxn], n;
     8 int mmin(int a,int b)
     9 {
    10     return a>b?b:a;
    11 }
    12 int mmax(int a, int b)
    13 {
    14     return a<b?b:a;
    15 }
    16 void Lcs()
    17 {
    18     int i, j;
    19     for(i = 1; i <= n; i++)
    20         for(j = 1; j <= n; j++)
    21             if(s1[i] == s2[j])
    22                 d[i%2][j] = d[(i-1)%2][j-1] + 1;
    23             else
    24                 d[i%2][j] = mmax(d[(i-1)%2][j], d[(i%2)][j-1]);
    25 }
    26 int main()
    27 {
    28     int i;
    29     while(cin>>n)
    30     {
    31         memset(d, 0, sizeof(d));
    32         for(i=1; i<=n; i++)
    33             cin>>s1[i];
    34         for(i = n; i >=1; i--)
    35             s2[i] = s1[n-i+1];
    36         Lcs();
    37         cout<<n-d[n%2][n]<<endl;
    38     }
    39     return 0;
    40 }

    方法二:

    这个是discuss里的方法。

    设ch[1]..ch[n]表示字符串1至n位,i为左游标,j为右游标 ,则i从n递减,j从i开始递增。
    min[i][j]表示i和j之间至少需要插入多少个字符才能对称,初始置全0 ,我们最终需要得到的值是min[1][n].
    则
    if(ch[i]==ch[j])                                    //如果两个游标所指字符相同,向中间缩小范围
        min[i][j]=min[i+1][j-1];
    else
         min[i][j] = 1 +  (min[i+1][j]和min[i][j-1]中的较小值);     //如果不同,典型的状态转换方程

     下面这个代码 是用的short int d[5000][5000]

    在poj 可以水过,但是在hdu还超内存,,所有需要用滚动数组来 节省内存。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 
     6 int mmin(int a,int b)
     7 {
     8     return a>b?b:a;
     9 }
    10 short int d[5010][5010];
    11 int main()
    12 {
    13     int n,i,j;
    14     char s[5010];
    15     memset(d,0,sizeof(d));
    16     cin>>n;
    17     for(i=1; i<=n; i++)
    18     cin>>s[i];
    19     for(i=n; i>=1; i--)
    20     for(j=i+1; j<=n; j++)
    21     if(s[i]==s[j])
    22     d[i][j]=d[i+1][j-1];
    23     else
    24     d[i][j]=mmin(d[i+1][j],d[i][j-1])+1;
    25 
    26     cout<<d[1][n]<<endl;
    27     return 0;
    28 }

    因为d[i][j] 算的时候只是与 d[i+1][j] 和 d[i][j+1]有关,所有可以开一个d[2][5000]的数组。

    滚动数组节省内存:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 using namespace std;
     5 
     6 int mmin(int a,int b)
     7 {
     8     return a>b?b:a;
     9 }
    10 int d[2][5010];
    11 int main()
    12 {
    13     int n,i,j;
    14     char s[5010];
    15     while(cin>>n)
    16     {
    17         memset(d, 0, sizeof(d));
    18         for(i=1; i<=n; i++)
    19             cin>>s[i];
    20 
    21         for(i=n; i>=1; i--)
    22             for(j=i+1; j<=n; j++)
    23                 if(s[i]==s[j])
    24                     d[i%2][j]=d[(i+1)%2][j-1];
    25                 else
    26                     d[i%2][j]=mmin(d[(i+1)%2][j],d[i%2][j-1])+1;
    27 
    28         cout<<d[1][n]<<endl;
    29     }
    30     return 0;
    31 }

    贴一下别人博客里的滚动数组的介绍:

    滚动数组 举个简单的例子:
    int i,d[100];
    d[0]=1;d[1]=1;
    for(i=2;i<100;i++)
    d[i]=d[i-1]+d[i-2];
    printf("%d",d[99]);
    上面这个循环d[i]只需要解集中的前2个解d[i-1]和d[i-2];
    为了节约空间用滚动数组的方法


    int d[3];
    d[0]=1;d[1]=1;
    for(i=2;i<100;i++)
    d[i%3]=d[(i-1)%3]+d[(i-2)%3];
    printf("%d",d[99%3]);


    注意上面的运算,我们只留了最近的3个解,数组好象在“滚动?一样,所以叫滚动数组


    对于二维数组也可以用这种方法 例如:
    int i,j,d[100][100];
    for(i=1;i<100;i++)
        for(j=0;j<100;j++)
            d[i][j]=d[i-1][j]+d[i][j-1];


    上?的d[i][j]忪便赖于d[i-1][j],d[i][j-1];
    迿用滚动数组
    int i,,j,d[2][100];
    for(i=1;i<100;i++)
        for(j=0;j<100;j++)
            d[i%2][j]=d[(i-1)%2][j]+d[i%2][j-1];


    滚动数组实际是一种节约空间的办法,时间上没什么优势,多用于DP中,举个例子先: 
    一个DP,平常如果需要1000×1000的空间,其实根据DP的特点,能以2×1000的空间解决问题,并且通过滚动,获得和1000×1000一样的效果。

  • 相关阅读:
    C++设计模式-Bridge桥接模式
    解决VS2010打开Web页面时经常由于内存较低而导致VS2010自动关闭的问题
    Js继承小结
    MAC上的包管理利器
    Objective-C的hook方案(一): Method Swizzling
    OleContainer操作Excel以二进制方式读写数据库
    复制文件时,如何显示进度条(使用TFileStream一点一点读,或者使用BlockRead,并插入application.ProcessMessages)
    ADO异步查询显示进度条
    D2010 RTTI + Attribute 简单实现ORM
    Delphi默认窗体随想
  • 原文地址:https://www.cnblogs.com/bfshm/p/3671875.html
Copyright © 2020-2023  润新知