zoukankan      html  css  js  c++  java
  • 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

    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

    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

    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. }  

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

    today lazy . tomorrow die .
  • 相关阅读:
    常用快捷键
    定时器
    Thread
    io流
    java错误
    Android设置背景图像重复【整理自网络】
    Win7构造wifi热点【Written By KillerLegend】
    Use a layout_width of 0dip instead of wrap_content for better performance.......【Written By KillerLegend】
    关于查看Android系统源码【Written By KillerLegend】
    Android:什么是Holo?【Translated By KillerLegend】
  • 原文地址:https://www.cnblogs.com/france/p/4808758.html
Copyright © 2011-2022 走看看