POJ1159,动态规划经典题目,很适合初学者入门练手。

求:为了使字符串左右对称,应该插入的最小字符数目。

设字符串为S1 S2 S3 … Sn. 这个字符串有n个字符,根据DP的基本思路,减少问题规模。如果S1和Sn匹配,则只关心S2 S3 …Sn-1,就这样问题规模减少了。如果S1和Sn不匹配,那就有两种办法。

方法1:加入S1’,字符串成S1S2 S3 … Sn S1’,则问题转化为S2 S3 … Sn。

方法2:加入Sn’,字符串成Sn’S1S2 S3 … Sn,则问题转化为S1 S3 … Sn-1。

显然,最终结果就是两种方法选最优。

设dp( i, j )表示Si … Sj要变成左右对称的字符串,需要插入的最少字符数目。

状态转换方程

dp( i, j ) = dp( i+1, j-1 )                        if Si == Sj

dp( i, j ) =Min( dp( i+1, j ), dp( i, j-1 ) ) + 1         if Si != Sj

编程实现:采用记忆化递归,比较简单,直接看代码把。

POJ1458,最长公共子序列( LCS ),算法导论上的例子。少说废话,直接看题吧。

设字符串X = X1 X2 X3 … Xn,  Y = Y1 Y2 Y3 … Ym.

如果Xn和Ym匹配,那么结果必然是X1 X2 … Xn-1和 Y1 Y2 … Ym-1的LCS + Xn

如果Xn和Ym不匹配,还是有两种选择:

选择1:

LCS的最后一个字符和Xn相同,则问题变成求X1 … Xn和 Y1 … Ym-1的LCS

选择2:

LCS的最后一个字符和Ym相同,则问题变成求X1 … Xn-1和 Y1 … Ym的LCS

设dp( i, j )表示X1 … Xi和 Y1 … Yj的最长公共子序列( LCS )的长度。

状态转换方程:

dp( i, j ) = dp( i+1, j-1 ) +1                        if Si == Sj

dp( i, j ) = Max( dp( i+1, j ), dp( i, j-1 ) )              if Si != Sj

呃,是不是感觉和上面的POJ1159有些相似?恩,确实有异曲同工之妙,慢慢体会吧。

编程实现:直接看下文代码。

POJ1141,brackets sequence,括号比配的问题。这题与上面两题有点像,有了上面两题的基础,分析此题也不难。好了,还是看题吧。

求:为了使原来的括号序列匹配,需求加入了最少括号数,而且要知道具体怎么加括号。

因为这题需要打印最终的匹配结果,所以在用DP的时候要多记录一些信息,以方便打印。

设原括号序列为S1 S2 … Sn。

如果S1和 Sn匹配,则相当于求S2 … Sn-1的括号匹配情况。这时最终的匹配结果是:先打印S1,再打印S2 … Sn-1的括号匹配结果,最后打印Sn。

如果S1和 Sn不匹配,怎么办呢?如果把S1 S2 … Sn从中间某个位置(比如Sk)分成两截,问题就变成S1 … Sk和Sk+1 … Sn的情况了。也就是说,把原问题划分成了两个结构相同的子问题。那么,具体从哪划分呢?好像没有什么信息可用,那就从1…n对k进行枚举。因为最终要打印结果,所以还要记录k的值。这时最终的结果是:先打印S1 … Sk的匹配情况,再打印Sk+1 … Sn的匹配情况。

设dp( i, j )表示Si … Sj匹配时,所要加入的最少括号数。

状态转换方程:

dp( i, j ) = dp( i+1, j-1 )                               ifS1和 Sn匹配

dp( i, j ) = Min( dp( i, k ) + dp( k+1, j ) ),   其中 i <= k < j    ifS1和 Sn不匹配

下图是字符串( [ ( ]的计算过程。

编程实现:算法是有了,不过具体的编程实现还是有点小技巧。嘿嘿,当前主要是针对初学者来说,大牛可完全无视之。

初始条件。当i==j时,dp( i, j ) = ?想想实际情况,只剩下一个括号时,不管它是什么当然不匹配啦。所以必须找到它的另一半才行,故dp( i, i ) = 1

计算顺序。应该沿Z型计算,即i、j之间相差1,i、j之间相差2,…

打印结果。使用递归打印。

POJ1159

  1. #include <iostream>
  2. using namespace std;
  3. //***********************常量定义*****************************
  4. const int MAX = 5000;
  5. //*********************自定义数据结构*************************
  6. //********************题目描述中的变量************************
  7. int size;
  8. char str[MAX];
  9. //**********************算法中的变量**************************
  10. short dp[MAX][MAX];
  11. //***********************算法实现*****************************
  12. //记忆化递归
  13. short Solve( int begin, int end )
  14. {
  15. //如果已经计算过,则直接返回
  16. if( dp[begin][end] >= 0 ) return dp[begin][end];
  17. //如果串的长度为1
  18. if( begin >= end ) return dp[begin][end] = 0;
  19. //如果该串的第一个字符和最后一个字符相同
  20. if( str[begin] == str[end] )
  21. {
  22. return dp[begin+1][end-1] = Solve( begin+1, end-1 );
  23. }
  24. else
  25. {
  26. short x = Solve( begin, end-1 );
  27. short y = Solve( begin+1, end );
  28. return dp[begin][end] = ( x < y ) ? ( x + 1 ) : ( y + 1 );
  29. }
  30. }
  31. //************************main函数****************************
  32. int main()
  33. {
  34. //freopen( "in.txt", "r", stdin );
  35. cin >> size;
  36. for( int i=0; i<size; i++ )
  37. {
  38. cin >> str[i];
  39. }
  40. memset( dp, -1, sizeof(dp) );
  41. cout << Solve( 0, size-1 ) << endl;
  42. return 0;
  43. }

POJ1458

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. //***********************常量定义*****************************
  5. const int MAX_SIZE = 500;
  6. //*********************自定义数据结构*************************
  7. //********************题目描述中的变量************************
  8. string strX;
  9. string strY;
  10. //**********************算法中的变量**************************
  11. //dp[i][j]表示X0 X1 ... Xi-1和Y0 Y1 ... Yj-1的最大公共子序列的长度
  12. //即dp[i][j]表示X的前i个字符和Y的前j个字符的最大公共子序列的长度
  13. int dp[MAX_SIZE][MAX_SIZE];
  14. //***********************算法实现*****************************
  15. void Solve()
  16. {
  17. int sizeX = (int)strX.size();
  18. int sizeY = (int)strY.size();
  19. //如果有一个串为空,则直接打印结果
  20. if( sizeX == 0 || sizeY == 0 )
  21. {
  22. cout << 0 << endl;
  23. return;
  24. }
  25. //由状态转换方程计算dp[i][j]
  26. for( int i=1; i<=sizeX; i++ )
  27. {
  28. for( int j=1; j<=sizeY; j++ )
  29. {
  30. //计算dp[i][j],要判断Xi-1和Yj-1是否相同
  31. if( strX[i-1] == strY[j-1] )
  32. {
  33. dp[i][j] = dp[i-1][j-1] + 1;
  34. }
  35. else
  36. {
  37. dp[i][j] = ( dp[i-1][j] > dp[i][j-1] ) ? dp[i-1][j] : dp[i][j-1];
  38. }
  39. }
  40. }
  41. cout << dp[sizeX][sizeY] << endl;
  42. }
  43. //************************main函数****************************
  44. int main()
  45. {
  46. //freopen( "in.txt", "r", stdin );
  47. while( cin >> strX >> strY )
  48. {
  49. Solve();
  50. }
  51. return 0;
  52. }

POJ1141

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. //***********************常量定义*****************************
  5. const int MAX = 105;
  6. const int INF = 999999999;
  7. //*********************自定义数据结构*************************
  8. //********************题目描述中的变量************************
  9. string str;
  10. //**********************算法中的变量**************************
  11. //设str = S0 S1 S2 ... Sn;
  12. //则dp[i][j]表示Si...Sj要构成最短正则括号序列所要增加的括号数目
  13. int dp[MAX][MAX];
  14. //pos[i][j]表示划分str成为两部分的最佳位置
  15. int pos[MAX][MAX];
  16. //***********************算法实现*****************************
  17. void DPSolve()
  18. {
  19. int size = (int)str.size();
  20. //只要一个括号时,必不匹配,要匹配需要另外一个括号
  21. for( int i=0; i<size; i++ )
  22. {
  23. dp[i][i] = 1;
  24. }
  25. //沿之字形填写dp[][]
  26. for( int k=1; k<size; k++ )
  27. {
  28. for( int i=0, j=i+k; i<size && j<size; i++, j++ )
  29. {
  30. //因为要求最小,现将dp[i][j]设置为最大
  31. dp[i][j] = INF;
  32. if( ( str[i] == '(' && str[j] == ')' ) || ( str[i] == '[' && str[j] == ']' ) )
  33. {
  34. dp[i][j] = dp[i+1][j-1];
  35. pos[i][j] = -1;
  36. }
  37. //注意:这里不要用else
  38. //因为要求最小,所以即使比配也要进行下面的处理:例如[][]
  39. //枚举tmp,求划分str的最佳位置
  40. for( int tmp=i; tmp<j; tmp++ )
  41. {
  42. if( dp[i][j] > dp[i][tmp] + dp[tmp+1][j] )
  43. {
  44. dp[i][j] = dp[i][tmp] + dp[tmp+1][j];
  45. pos[i][j] = tmp;
  46. }
  47. }
  48. }
  49. }
  50. }
  51. //根据dp[][],打印结果
  52. void Print( int left, int right )
  53. {
  54. if( left <= right )
  55. {
  56. //当只有一个括号时,直接打印
  57. if( left == right )
  58. {
  59. if( str[left] == '(' || str[left] == ')' ) cout << "()";
  60. if( str[left] == '[' || str[left] == ']' ) cout << "[]";
  61. }
  62. else
  63. {
  64. //如果首尾括号匹配
  65. if( pos[left][right] == -1 )
  66. {
  67. cout << str[left];
  68. Print( left+1, right-1 );
  69. cout << str[right];
  70. }
  71. else
  72. {
  73. Print( left, pos[left][right] );
  74. Print( pos[left][right]+1, right );
  75. }
  76. }
  77. }
  78. }
  79. //************************main函数****************************
  80. int main()
  81. {
  82. //freopen( "in.txt", "r", stdin );
  83. cin >> str;
  84. DPSolve();
  85. Print( 0, str.size()-1 );
  86. cout << endl;
  87. return 0;
  88. }

版权声明:本文为博主原创文章,未经博主允许不得转载。

DP---(POJ1159 POJ1458 POJ1141)的更多相关文章

  1. 11月下旬poj其他题

    poj1000,poj1003,poj1004,poj1064,poj1218 水题 poj1012:0<k<14——漂亮的打表 poj1651:与能量项链很像的dp poj1159:回文 ...

  2. POJ1159:Palindrome【dp】

    题目大意:给出一个字符串,问至少添加多少个字符才能使它成为回文串? 思路:很明显的方程是:dp[i][j]=min{dp[i+1][j],dp[i][j-1],dp[i+1][j-1](str[i]= ...

  3. poj1141 区间dp+路径

    //Accepted 176 KB 47 ms //感谢大神们为我们这群渣渣铺平前进的道路!! //用scanf("%s",s)!=EOF WA到死 #include <cs ...

  4. poj1159 dp最长公共子串

    //Accepted 204 KB 891 ms //dp最长公共子串 //dp[i][j]=max(dp[i-1][j],dp[i][j-1]) //dp[i][j]=max(dp[i][j],dp ...

  5. [原]POJ1141 Brackets Sequence (dp动态规划,递归)

    本文出自:http://blog.csdn.net/svitter 原题:http://poj.org/problem?id=1141 题意:输出添加括号最少,并且使其匹配的串. 题解: dp [ i ...

  6. [POJ1159]Palindrome(dp,滚动数组)

    题目链接:http://poj.org/problem?id=1159 题意:求一个字符串加多少个字符,可以变成一个回文串.把这个字符串倒过来存一遍,求这两个字符串的lcs,用原长减去lcs就行.这题 ...

  7. POJ1159 - Palindrome(区间DP)

    题目大意 给定一个字符串S,问最少插入多少个字符可以使字符串S变为回文串 题解 用dp[i][j]表示把字符串s[i-j]变为回文串需要插入的最小字符数 如果s[i]==s[j]那么dp[i][j]= ...

  8. POJ1159 Palindrome(dp)

    题目链接. 分析: 感叹算法的力量. 方法一: 设 dp[i][j] 为字符串 s, 从 i 到 j 需要添加的最少字符数. 那么如果 s[i] == s[j], dp[i][j] = dp[i+1] ...

  9. POJ1159 Palindrome(数位DP)

    Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 58277   Accepted: 20221 Desc ...

随机推荐

  1. HCNA(一)网络传输介质

    一 .同轴线缆 介绍:同轴线缆是一种早期的网络传输介质,同轴电缆的得名与它的结构相关,由内导体.外导体.绝缘介质和防护套四部分组成.同样支持10Mbps传输速率.现在已经基本被淘汰,不在应用于企业网络 ...

  2. Java实例 Part4:数组及其常用操作

    目录 Part4:数组及其常用操作 Example01:将二维数组的行列交换 Example02:使用选择排序法对数组进行排序 Example03:使用冒泡排序法对数组进行排序 Example04:使 ...

  3. 大数据:Map终结和Spill文件合并

    当Mapper没有数据输入,mapper.run中的while循环会调用context.nextKeyValue就返回false,于是便返回到runNewMapper中,在这里程序会关闭输入通道和输出 ...

  4. 搭建最小linux系统

    Busybox简介 • 制作文件系统我们需要使用到Busybox 工具 – 版本为busybox-1.21.1.tar.bz2 – 开源网址是http://www.busybox.net/ – Bus ...

  5. 话说文件系统——VFS简介(二)

    linux可以与很多文件系统完美的结合,可以很容易地把Windows.其他Unix系统.甚至在市场上很小众的文件系统轻松地移植到linux中. 这对于linux今天的成功是功不可没的,那为什么这么厉害 ...

  6. WPF实现拖拽功能

    技术点:WPF的Behaviors实现了对象的行为附加,Microsoft.Expression.Interactions程序集中包含了若干Behaviors,其中MouseDragElementBe ...

  7. 20155316 2016-2017-2 《Java程序设计》第2周学习总结

    教材学习内容总结 学习主要内容:基本类型介绍及流程控制简介 关键点:关键记住JAVA的大体框架,可以类比C语言结合着记.相较于C不同且值得关注的主要信息有: 基本类型的不同:byte.boolean. ...

  8. 如何指定rman下的备份路径

    如果不想使用缺省路径,可以以如下方式来指定: RMAN> configure channel 1 device type disk format '/rman/bak/%F';RMAN> ...

  9. tidb测试环境安装,离线部署

    1.环境以及规划 机器:centos7.5 ; 文件系统为ext4:内存16g:cpu8核,共三个节点: ip hostname roles --- tidb tipd tikv --- tidb t ...

  10. 1、maven打包 install package deploy区别

    maven package:打包到本项目,一般是在项目target目录下.如果a项目依赖于b项目,打包b项目时,只会打包到b项目下target下,编译a项目时就会报错. maven install:打 ...