1. 程式人生 > >Brakets Sequence(DP)

Brakets Sequence(DP)

【題意】
定義如下正規括號序列

  • 空序列是正規括號序列
  • 如果S是正規括號序列,那麼[S], (S)也是正規括號序列
  • 如果A和B都是正規括號序列,那麼AB也是正規括號序列

輸入一個長度不超過100的只有’(‘, ‘)’, ‘[‘, ‘]’組成的字串序列,新增最少的括號得到一個規則序列,如果有多解輸出任意一組即可

【思路】
設將串S變為正規序列至少需要dp(S)個括號,那麼

  • 如果S的結構是(S’)或[S’],那麼就可以轉移到dp(S’)
  • 如果S至少有兩個字元,那麼可以劃分為兩個部分AB,轉移到dp(A)+dp(B)

程式設計實現的時候,dp(i,j)表示的是把s[i~j]變成正規括號序列所需要新增的最少括號個數,狀態轉移方程為
dp(i,j)=min{  dp(i+1,j-1), s[i]和s[j]匹配
                  dp(i,k)+dp(k+1,j),i<=k< j }

列印解的時候用遞迴函式列印,我感覺這個思路很巧妙,自己很難想到,基本上是把紫書的程式碼抄了一遍,然後下面的程式碼用C++11能過,用C++會編譯錯誤,會在gets()這裡報錯,然而並不知道這是為什麼

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

const int inf=2e9;
const int maxn=105;

char s[maxn];
int
dp[maxn][maxn]; bool match(char c1,char c2){ if(c1=='(' && c2==')') return true; if(c1=='[' && c2==']') return true; return false; } void print(int i,int j){//列印s[i~j]的解 if(i>j) return; if(i==j){ if(s[i]=='(' || s[i]==')') printf("()"); else printf
("[]"); return; } int ans=dp[i][j]; if(match(s[i],s[j]) && ans==dp[i+1][j-1]) { printf("%c",s[i]); print(i+1,j-1); printf("%c",s[j]); return; } for(int k=i;k<j;++k){ if(ans==dp[i][k]+dp[k+1][j]) { print(i,k); print(k+1,j); return; } } } int main(){ int t; scanf("%d",&t); getchar(); while(t--){ getchar(); gets(s); int len=strlen(s); memset(dp,0,sizeof(dp)); for(int i=0;i<len;++i) dp[i][i]=1; for(int L=1;L<len;++L){ for(int i=0;i+L<len;++i){ int j=i+L; dp[i][j]=inf; if(match(s[i],s[j])) dp[i][j]=min(dp[i][j],dp[i+1][j-1]); for(int k=i;k<j;++k) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); } } print(0,len-1); printf("\n"); if(t) printf("\n"); } return 0; }