UVA1626 Brackets sequence

本篇为刘汝佳《算法竞赛入门经典(第2版)》P278 的读书笔记。

解题思路:

  我们用 (dp(x)(y)) 来记录字符串 (s) 中从位置 (x) 到位置 (y) 需要添加的字符数(其实就是这一段字符串的 “字符失配数”)。

  DP部分:

  状态转移方程为 (dp(i)(j) = min{dp(i)(k) + dp(k+1)(j) mid i le k < j }),如果字符 (s[i]、s[j]) 相匹配,那么在进行了上述的转移以后也不能忘了 (dp(i+1)(j-1)) 这种情况,即 (dp(i)(j) = min{dp(i)(j), dp(i+1)(j-1)}),因为它的首末字母已经确定匹配了,那么 “失配数” 就看中间的那部分了。如果考虑的这段字符串的首末字母匹配了,是不是就不用进行前面的 (dp(i)(j) = min{dp(i)(k) + dp(k+1)(j) mid i le k < j }) 这一段状态转移了呢?不是的。举个反例,如果我们不进行第一种转移,那么 “[][]" 就会直接转移到 “][”,然后就得加2个括号了。

  具体实现方法是外层循环从短到长遍历各种字符串长度,第二层循环遍历首位(末位随之确定),最内层循环遍历字符串中的各位元素。

  输出部分:

  本题的难点之所在。书中介绍了一种有点像分治法的方法,在打印时对照着我们得出的 (dp) 数组重新检查一下哪个决策最好。当打印 (i) 到 (j) 这部分的字符串的时候,从头到尾遍历字符串,当找到位置 (k) 使得 (dp(i)(k) + dp(k+1)(j) == dp(i)(j)),便可退出循环,将 (print(i,j)) 分成 (print(i,k)) 和 (print(k+1,j));当然,如果字符串首末匹配并且 (dp(i)(j) == dp(i+1)(j-1)),那么问题就变成打印首末字母,在中间考虑 (print(i+1,j-1))。边界情况:(i>j) 时说明上一层刚好解决了类似 “[]" 或者 "()" 的情况,于是我们什么都不用输出;(i==j) 时,我们就要给字符串加匹配字符了,这些都是匹配剩下的字符。

  danger!!! 这题的输入输出格式是个坑!请读者自行体会。

AC代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 
 5 using namespace std;
 6 const int maxn=110, inf=0x7fffffff;
 7 char inp[maxn];
 8 int dp[maxn][maxn];
 9 bool match(char a,char b){
10     if(a=='('&&b==')')  return true;
11     if(a=='['&&b==']')  return true;
12     return false;
13 }
14 void print(int x,int y){
15     if(x>y) return;
16     if(x==y){
17         if(inp[x]=='('||inp[x]==')')    printf("()");
18         else   printf("[]");
19         return;
20     }
21     if(match(inp[x],inp[y])&&dp[x][y]==dp[x+1][y-1]){
22         printf("%c",inp[x]);    print(x+1,y-1); printf("%c",inp[y]);
23         return;
24     }
25     for(int k=x;k<y;k++){
26         if(dp[x][y]==dp[x][k]+dp[k+1][y]){
27             print(x,k); print(k+1,y);
28             return;
29         }
30     }
31 }
32 int main(){
33     int kase;
34     int flag=0;
35     scanf("%d",&kase);
36     getchar();
37     while(kase--){
38         gets(inp);
39         gets(inp);
40         int len=strlen(inp);
41         if(len==0){
42             if(flag++)    printf("
");
43             printf("
");
44         }
45         else{
46             for(int i=0;i<len;i++){
47                 dp[i+1][i]=0;
48                 dp[i][i]=1;
49                 if(i<len-1){
50                     if(match(inp[i],inp[i+1]))  dp[i][i+1]=0;
51                     else    dp[i][i+1]=2;
52                 }
53             }
54             for(int i=2;i<len;i++){
55                 for(int j=0;j+i<len;j++){
56                     dp[j][j+i]=len;
57                     if(match(inp[j],inp[j+i]))  dp[j][j+i]=min(dp[j][j+i],dp[j+1][j+i-1]);
58                     for(int k=j;k<j+i;k++)
59                         dp[j][j+i]=min(dp[j][j+i],dp[j][k]+dp[k+1][j+i]);
60                 }
61             }
62             if(flag++)   printf("
");
63             print(0,len-1);
64             printf("
");
65         }
66     }
67     return 0;
68 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/7705576.html