• 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 }
    “这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
  • 相关阅读:
    Android x86 镜像分析之四
    Android x86 镜像分析之三
    Android x86 镜像分析之二
    Android x86镜像分析
    tizen镜像制作
    完成登录与注册页面的前端
    JavaScript 基础,登录前端验证
    CSS实例:图片导航块
    导航,头部,CSS基础
    web基础,用html元素制作web页面
  • 原文地址:https://www.cnblogs.com/Blogggggg/p/7705576.html
Copyright © 2020-2023  润新知