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
- #include <iostream>
- using namespace std;
- //***********************常量定义*****************************
- const int MAX = 5000;
- //*********************自定义数据结构*************************
- //********************题目描述中的变量************************
- int size;
- char str[MAX];
- //**********************算法中的变量**************************
- short dp[MAX][MAX];
- //***********************算法实现*****************************
- //记忆化递归
- short Solve( int begin, int end )
- {
- //如果已经计算过,则直接返回
- if( dp[begin][end] >= 0 ) return dp[begin][end];
- //如果串的长度为1
- if( begin >= end ) return dp[begin][end] = 0;
- //如果该串的第一个字符和最后一个字符相同
- if( str[begin] == str[end] )
- {
- return dp[begin+1][end-1] = Solve( begin+1, end-1 );
- }
- else
- {
- short x = Solve( begin, end-1 );
- short y = Solve( begin+1, end );
- return dp[begin][end] = ( x < y ) ? ( x + 1 ) : ( y + 1 );
- }
- }
- //************************main函数****************************
- int main()
- {
- //freopen( "in.txt", "r", stdin );
- cin >> size;
- for( int i=0; i<size; i++ )
- {
- cin >> str[i];
- }
- memset( dp, -1, sizeof(dp) );
- cout << Solve( 0, size-1 ) << endl;
- return 0;
- }
POJ1458
- #include <iostream>
- #include <string>
- using namespace std;
- //***********************常量定义*****************************
- const int MAX_SIZE = 500;
- //*********************自定义数据结构*************************
- //********************题目描述中的变量************************
- string strX;
- string strY;
- //**********************算法中的变量**************************
- //dp[i][j]表示X0 X1 ... Xi-1和Y0 Y1 ... Yj-1的最大公共子序列的长度
- //即dp[i][j]表示X的前i个字符和Y的前j个字符的最大公共子序列的长度
- int dp[MAX_SIZE][MAX_SIZE];
- //***********************算法实现*****************************
- void Solve()
- {
- int sizeX = (int)strX.size();
- int sizeY = (int)strY.size();
- //如果有一个串为空,则直接打印结果
- if( sizeX == 0 || sizeY == 0 )
- {
- cout << 0 << endl;
- return;
- }
- //由状态转换方程计算dp[i][j]
- for( int i=1; i<=sizeX; i++ )
- {
- for( int j=1; j<=sizeY; j++ )
- {
- //计算dp[i][j],要判断Xi-1和Yj-1是否相同
- if( strX[i-1] == strY[j-1] )
- {
- dp[i][j] = dp[i-1][j-1] + 1;
- }
- else
- {
- dp[i][j] = ( dp[i-1][j] > dp[i][j-1] ) ? dp[i-1][j] : dp[i][j-1];
- }
- }
- }
- cout << dp[sizeX][sizeY] << endl;
- }
- //************************main函数****************************
- int main()
- {
- //freopen( "in.txt", "r", stdin );
- while( cin >> strX >> strY )
- {
- Solve();
- }
- return 0;
- }
POJ1141
- #include <iostream>
- #include <string>
- using namespace std;
- //***********************常量定义*****************************
- const int MAX = 105;
- const int INF = 999999999;
- //*********************自定义数据结构*************************
- //********************题目描述中的变量************************
- string str;
- //**********************算法中的变量**************************
- //设str = S0 S1 S2 ... Sn;
- //则dp[i][j]表示Si...Sj要构成最短正则括号序列所要增加的括号数目
- int dp[MAX][MAX];
- //pos[i][j]表示划分str成为两部分的最佳位置
- int pos[MAX][MAX];
- //***********************算法实现*****************************
- void DPSolve()
- {
- int size = (int)str.size();
- //只要一个括号时,必不匹配,要匹配需要另外一个括号
- for( int i=0; i<size; i++ )
- {
- dp[i][i] = 1;
- }
- //沿之字形填写dp[][]
- for( int k=1; k<size; k++ )
- {
- for( int i=0, j=i+k; i<size && j<size; i++, j++ )
- {
- //因为要求最小,现将dp[i][j]设置为最大
- dp[i][j] = INF;
- if( ( str[i] == '(' && str[j] == ')' ) || ( str[i] == '[' && str[j] == ']' ) )
- {
- dp[i][j] = dp[i+1][j-1];
- pos[i][j] = -1;
- }
- //注意:这里不要用else
- //因为要求最小,所以即使比配也要进行下面的处理:例如[][]
- //枚举tmp,求划分str的最佳位置
- for( int tmp=i; tmp<j; tmp++ )
- {
- if( dp[i][j] > dp[i][tmp] + dp[tmp+1][j] )
- {
- dp[i][j] = dp[i][tmp] + dp[tmp+1][j];
- pos[i][j] = tmp;
- }
- }
- }
- }
- }
- //根据dp[][],打印结果
- void Print( int left, int right )
- {
- if( left <= right )
- {
- //当只有一个括号时,直接打印
- if( left == right )
- {
- if( str[left] == '(' || str[left] == ')' ) cout << "()";
- if( str[left] == '[' || str[left] == ']' ) cout << "[]";
- }
- else
- {
- //如果首尾括号匹配
- if( pos[left][right] == -1 )
- {
- cout << str[left];
- Print( left+1, right-1 );
- cout << str[right];
- }
- else
- {
- Print( left, pos[left][right] );
- Print( pos[left][right]+1, right );
- }
- }
- }
- }
- //************************main函数****************************
- int main()
- {
- //freopen( "in.txt", "r", stdin );
- cin >> str;
- DPSolve();
- Print( 0, str.size()-1 );
- cout << endl;
- return 0;
- }
版权声明:本文为博主原创文章,未经博主允许不得转载。
DP---(POJ1159 POJ1458 POJ1141)的更多相关文章
- 11月下旬poj其他题
poj1000,poj1003,poj1004,poj1064,poj1218 水题 poj1012:0<k<14——漂亮的打表 poj1651:与能量项链很像的dp poj1159:回文 ...
- POJ1159:Palindrome【dp】
题目大意:给出一个字符串,问至少添加多少个字符才能使它成为回文串? 思路:很明显的方程是:dp[i][j]=min{dp[i+1][j],dp[i][j-1],dp[i+1][j-1](str[i]= ...
- poj1141 区间dp+路径
//Accepted 176 KB 47 ms //感谢大神们为我们这群渣渣铺平前进的道路!! //用scanf("%s",s)!=EOF WA到死 #include <cs ...
- 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 ...
- [原]POJ1141 Brackets Sequence (dp动态规划,递归)
本文出自:http://blog.csdn.net/svitter 原题:http://poj.org/problem?id=1141 题意:输出添加括号最少,并且使其匹配的串. 题解: dp [ i ...
- [POJ1159]Palindrome(dp,滚动数组)
题目链接:http://poj.org/problem?id=1159 题意:求一个字符串加多少个字符,可以变成一个回文串.把这个字符串倒过来存一遍,求这两个字符串的lcs,用原长减去lcs就行.这题 ...
- POJ1159 - Palindrome(区间DP)
题目大意 给定一个字符串S,问最少插入多少个字符可以使字符串S变为回文串 题解 用dp[i][j]表示把字符串s[i-j]变为回文串需要插入的最小字符数 如果s[i]==s[j]那么dp[i][j]= ...
- POJ1159 Palindrome(dp)
题目链接. 分析: 感叹算法的力量. 方法一: 设 dp[i][j] 为字符串 s, 从 i 到 j 需要添加的最少字符数. 那么如果 s[i] == s[j], dp[i][j] = dp[i+1] ...
- POJ1159 Palindrome(数位DP)
Palindrome Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 58277 Accepted: 20221 Desc ...
随机推荐
- node创建服务器
//引入核心模块 const http = require('http'); //创建服务器 http.createServer((req,res)=>{ }).listen(3000); // ...
- redis常用数据类型操作命令集锦
redis操作命令集锦 redis中五种数据类型 1) 字符串 String 特点: 存储所有的字符和字符串 应用场景: 做缓存使用 2) 哈希 hash 特点: 相当于java中hashMap集合 ...
- LEFT JOIN个别问题
SELECT a.loginuser, a.schoolid, count(b.id)FROM vhs_school AS aLEFT JOIN vhs_attence AS b ON a.schoo ...
- 大数据学习:Spark是什么,如何用Spark进行数据分析
给大家分享一下Spark是什么?如何用Spark进行数据分析,对大数据感兴趣的小伙伴就随着小编一起来了解一下吧. 大数据在线学习 什么是Apache Spark? Apache Spark是一 ...
- 实验吧 Fair Play
知识点:playfair密码以前没见过 Playfair密码(英文:Playfair cipher 或 Playfair square)是一种使用一个关键词方格来加密字符对的加密法,1854年由查尔斯 ...
- SQL 列转行 分组去重并合并多条记录
继上篇<SQL 列转行 合并多条记录>后,有网友反馈新的需求还是不太会用. 现举例说明 一,网友需要如下的效果: 其实,这个需求依然可以我上篇的方法进行解答,但为了实现分组,需要disti ...
- OpenFlow1.3.3 学习记录(持续更新)
OpenFlow1.3.3 学习记录(持续更新) 正在学习OpenFlow1.3,该篇笔记将日常更新,主要内容大致为官方文档的总结与翻译. 交换机组件 按照优先级顺序进行包匹配,如果匹配到流表项,则执 ...
- XAMPP设置tomcat自启动时,报无效的Win32程序
最近给一个客户开发了一套系统,需要在内网中部署.系统是Java + Tomcat7 + mysql开发的. 考虑到客户内网不能上网的情况下,想使用XAMPP的便捷性,给客户进行部署.因为只需要Tomc ...
- Java线程和多线程(十三)——Callable,Future,FutureTask
在Java多线程之中,Callable和Future的使用时非常广泛的.在之前的文章中,我们了解了关于Java线程池基础的一些内容,知道如何提交Runnable的任务.但是,Runnable的任务是无 ...
- 3110: [Zjoi2013]K大数查询
3110: [Zjoi2013]K大数查询 https://lydsy.com/JudgeOnline/problem.php?id=3110 分析: 整体二分+线段树. 两种操作:区间加入一个数,区 ...