uva
紫书例题,这个区间dp最容易错的应该是(S)这种匹配情况,如果不是题目中给了提示我就忽略了,只想着左右分割忘记了这种特殊的例子。
dp[i][j]=MIN{dp[i+1][j-1] | if(match(i,j) , dp[i][k]+dp[k+1][j] | i<=k<=j .}
注意初始化dp[i][i]=1,表示1个字符最少需要一个才能匹配,dp[i+1][i]=0,因为可能只有两个字符使得i+1>j-1,我们可以认为中间是空字符已经匹配了。
打印路径利用了递归,很巧妙,lrj的代码确实短小精悍。
还有就是本题的输入输出要注意,可能出现空串,输入的每一行字符间(包括第一行字符和t之间)都要键入一个空格,输出每两个答案之间输出一个空格。
为了防止getline()将输入的t读入到s中,我们将t以字符形式读入。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x3f3f3f3f 4 int dp[110][110]; 5 char s[115]; 6 bool match(int i,int j) 7 { 8 return (s[i]=='['&&s[j]==']')||(s[i]=='('&&s[j]==')'); 9 } 10 void print(int l,int r) 11 { 12 if(l>r) return; 13 if(l==r){ 14 if(s[l]=='['||s[r]==']') printf("[]"); 15 else printf("()"); 16 return; 17 } 18 if(dp[l][r]==dp[l+1][r-1]&&match(l,r)){ 19 printf("%c",s[l]); 20 print(l+1,r-1); 21 printf("%c",s[r]); 22 return; 23 } 24 for(int k=l;k<=r;++k){ 25 if(dp[l][r]==dp[l][k]+dp[k+1][r]){ 26 print(l,k); 27 print(k+1,r); 28 return; 29 } 30 } 31 } 32 int main() 33 { 34 int t,n,m=0,i,j,k; 35 cin.getline(s,105); 36 t=atoi(s); 37 while(t--){getchar();m++; 38 if(m>1) puts(""); 39 cin.getline(s+1,105); 40 n=strlen(s+1); 41 memset(dp,inf,sizeof(dp)); 42 for(i=0;i<=n;++i) 43 { 44 dp[i][i]=1; 45 dp[i+1][i]=0; 46 } 47 for(int len=2;len<=n;++len) 48 { 49 for(i=1,j=len;j<=n;++i,++j) 50 { 51 if(match(i,j)) dp[i][j]=min(dp[i][j],dp[i+1][j-1]); 52 for(k=i;k<=j;++k) 53 { 54 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 55 } 56 } 57 } 58 //cout<<dp[1][n]<<endl; 59 print(1,n);puts(""); 60 } 61 return 0; 62 }