poj1141Brackets Sequence 记忆化dp 打印路径 经典

输入括号

输入:(注意:据说有空行,用getline()或者gets()输入)

([(]

([(((])))

[]]]]]

 

)))(((

要求输出最小要加入的括号数目之后的匹配括号串

分析:

   经典dp题,如果用dp自底向上的递推做的话,比较麻烦,其实用记忆化做很简单,

   每次递归前加上判断是否已经计算过即可减少计算量,相当于dfs里的剪枝,代码如下

 

#include <iostream>

#include <string>

#include <cstring>

using namespace std;

#define X 205

#define INF 1000

int dp[X][X],pre[X][X];

string s;

int f(int i,int j) //记忆化

{

   if(dp[i][j]!=-1)            //当计算过时

      return dp[i][j];

   if(i>j)                     //当前面的指针大于后面的指针时,已经结束这次递归

      return 0;

   if(i==j)                 //恰巧相等时,加一

      return 1;

   int ans = INF;

   if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))

   {

      ans = f(i+1,j-1);  //当两者恰好能组成一对括号时

      pre[i][j] = -1;

   }

   for(int k=i;k<j;k++)        //相当于最短路时的松弛操作

   {

      if(ans>f(i,k)+f(k+1,j))

      {

         ans = f(i,k)+f(k+1,j);

         pre[i][j] = k;     //记录中间断开的点,递归打印括号

      }

   }

   dp[i][j] = ans;             //用辅助数组储存状态,思想为用空间换取时间

   return ans;

}

void print(int i,int j)

{

   if(i>j)

      return;

   if(i==j)           //但两者相同位置时,直接输出该对括号

   {

      if(s[i]=='('||s[i]==')')

         printf("()");

      else if(s[i]=='['||s[j]==']')

         printf("[]");

   }

   else if(pre[i][j]==-1)

   {     //当后面已有它匹配的串时,先输出该括号,再递归打印出中间的括号,再打印后面匹配的括号

      cout<<s[i];

      print(i+1,j-1);

      cout<<s[j];

   }

   else  //递归打印以k为分界的的括号

   {

      print(i,pre[i][j]);

      print(pre[i][j]+1,j);

   }

}

int main()

{

   freopen("sum.in","r",stdin);

   freopen("sum.out","w",stdout);

   while(getline(cin,s))

   {

      memset(pre,-1,sizeof(pre));

      memset(dp,-1,sizeof(dp));

      if(s.size())    //注意到有空行

      {

         f(0,s.size()-1);

         print(0,s.size()-1);

         cout<<endl;

      }

      else

         cout<<endl;

   }

   return 0;

}

原文地址:https://www.cnblogs.com/yejinru/p/2405783.html