DP---(POJ1159 POJ1458 POJ1141)

2024-06-14 08:32
文章标签 dp poj1141 poj1458 poj1159

本文主要是介绍DP---(POJ1159 POJ1458 POJ1141),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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


 POJ1458

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


POJ1141

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

这篇关于DP---(POJ1159 POJ1458 POJ1141)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1059894

相关文章

hdu4826(三维DP)

这是一个百度之星的资格赛第四题 题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1004&cid=500 题意:从左上角的点到右上角的点,每个点只能走一遍,走的方向有三个:向上,向下,向右,求最大值。 咋一看像搜索题,先暴搜,TLE,然后剪枝,还是TLE.然后我就改方法,用DP来做,这题和普通dp相比,多个个向上

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu4865(概率DP)

题意:已知前一天和今天的天气概率,某天的天气概率和叶子的潮湿程度的概率,n天叶子的湿度,求n天最有可能的天气情况。 思路:概率DP,dp[i][j]表示第i天天气为j的概率,状态转移如下:dp[i][j] = max(dp[i][j, dp[i-1][k]*table2[k][j]*table1[j][col] )  代码如下: #include <stdio.h>#include

usaco 1.1 Broken Necklace(DP)

直接上代码 接触的第一道dp ps.大概的思路就是 先从左往右用一个数组在每个点记下蓝或黑的个数 再从右到左算一遍 最后取出最大的即可 核心语句在于: 如果 str[i] = 'r'  ,   rl[i]=rl[i-1]+1, bl[i]=0 如果 str[i] = 'b' ,  bl[i]=bl[i-1]+1, rl[i]=0 如果 str[i] = 'w',  bl[i]=b

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

uva 10118 dP

题意: 给4列篮子,每次从某一列开始无放回拿蜡烛放入篮子里,并且篮子最多只能放5支蜡烛,数字代表蜡烛的颜色。 当拿出当前颜色的蜡烛在篮子里存在时,猪脚可以把蜡烛带回家。 问最多拿多少只蜡烛。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cs

uva 10069 DP + 大数加法

代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <queue>#include <map>#include <cl

uva 10029 HASH + DP

题意: 给一个字典,里面有好多单词。单词可以由增加、删除、变换,变成另一个单词,问能变换的最长单词长度。 解析: HASH+dp 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int