Blog

Uva1626 Brackets sequence

Dp

题意概括:加上最少的括号,使原来的残缺括号串变成合法的括号串,我们称之为最小括号串,例如([)]变成()[()]

假设最后生成的最小括号串是唯一的,那么其中每一个括号字符都有与它配对的括号字符,那么我们的目标其实就是找出原残缺括号串中,已经存在的最小括号串中的括号配对,然后再补全缺少配对的括号。例如,下面图片中红色中括号是残缺括号串中已有配对:

#

对于一个左括号,在残缺括号串中可能会存在多个右括号可以与之匹配。但在最小括号串中,只有其中一个右括号,或干脆没有(如上例)右括号,与这个左括号匹配。对于一个右括号也是同理。

当我们选定一对括号成为一组配对时,原括号串将被划为两个区域,即“括号内”与“括号外”,分别在两个区域中的左右括号将无法组成括号串

#

对于一个合法括号串,一定是由一个或多个同级的最外层括号组成的,如下图

#
注意这里说的是合法括号串,而不是最小括号串,上图每个括号里都可以再包含一个合法括号串,而这些合法括号串也可以有上图的特性 在最小括号串中的每个合法括号串(可以递归),都对应着残缺括号串中的局部。
#

我们的只要在对应的局部残缺括号串中,找出这些最外层括号之间的分割点。

d[i][j] 代表 i~j残缺括号子串 所需要补充括号的最少数量。

对于i~j残缺括号子串,如果找到正确的最外层括号分割点,则“需要补充的括号数量”最少

public static void dp() {
    for (int i = 0; i < len; i++) {
        d[i][i] = 1; //只有一个括号字符,必定需要补上另一半
        d[i+1][i] = 0; //已经是最里层的括号了
    }
    for(int start = len-2; start >= 0 ; start--) {
        for(int end = start+1; end < len; end++) {
            d[start][end] = len; // 最多增长为原来字符串的两倍
            // 分割括号串 的情况
            // 其实就是找出当前最外层括号对之间的分割点,正确的最外层分割点能让“需要补充的括号数量”最少
            for(int k = start; k < end; k++) {
                d[start][end] = Math.min(d[start][end], d[start][k] + d[k+1][end]);
            }
            // 不分割 且 两头刚好能组成一对括号的情况
            // 如果两头不能组成一对括号,或者不是最优配对法,那么最优配对已经在前一种(分割)的情况里了
            if(isBuddy(s[start], s[end])) {
                d[start][end] = Math.min(d[start][end], d[start+1][end-1]);
            }
        }
    }
}

未完待续 #

  1. 遍历顺序有讲究
  2. 对于找不到配对的括号,最好的配对方法,就是直接在他相邻位置补上一个配对的括号,此时自然就变成了AB形式的括号串,也就是在每个合法括号串的最外层。所以最后一张图片是错误的

Uva11400 Lighting System Design

Dp

题意概括:买一定数量的灯泡,通过将低电压灯泡可以替换为高电压灯泡来节省成本,反过来不行,每种电压的电源可以带动无数个该电压的灯泡

需要注意的是,每种电压的灯泡都可以有多种替换方案(包括“不替换“)来节省成本

  1. 排除“部分替换”,“间接替换”,“嵌套替换”方案,他们要不就是明显可以优化,要不就是不会减少成本
  2. 剩下一些需要具体计算的“次优方案”
  3. 最后比较“次有方案”得出全局相关的唯一“最优方案”

理解并排除以下三种非次优方案(可以证明一定不是最优方案的候选)非常关键:

  1. 不存在“部分替换”:每种灯泡的替换方式只有“全部替换”和“不替换”两种,但不可能“部分替换”
  2. 不存在“间接替换”:若一种灯泡作为被替换的目标,则它不再可能再替换为别的灯泡,相当于该种灯泡不替换
  3. 不存在“嵌套替换”:存在多种灯泡时,若低电压灯泡替换到高号灯泡,且中电压灯泡作为被替换到的目标或不替换(可以看作是一种“自替换”),则该方案一定不是最优方案,直接排除

我们接下来分别证明这三条结论。

排除“部分替换” #

不可能部分替换这一点非常好证明

现有两种电压的灯泡

序号 电压 电源价格 灯泡单价 所需数量
1 K1 C1 L1
2 K2 C2 L2

当 C1 >= C2 时,1号灯泡全部替换为2号灯泡明显优于部分替换(无论替换多少个),全部替换至少可以省下 K1 电源钱

当 C1 < C2 时,将1号灯泡部分替换2号灯泡,只是在徒增成本。此时该采用哪种方案的关键,就是判断 因灯泡单价上涨而所需的钱 是否大于 买电源的钱

  • 当 (C2 - C1) * L1 > K1 时,也就是替换灯泡多出来的钱,比买1号灯泡的电源还多,则选择“不替换“
  • 反之,选择“全部替换”,虽然灯泡单价更高,但通过不买1号灯泡的电源来省钱

得出结论一:每种灯泡的替换方式只有“全部替换”和“不替换”两种,但不可能“部分替换”

#

从此节之后,将不再讨论“部分替换”,如提到替换,必然是“全部替换”。

排除”间接替换“ #

序号 电压 电源价格 灯泡单价 所需数量
1 K1 C1 L1
2 K2 C2 L2
3 K3 C3 L3

对于上面三种灯泡,存在这样的次优替换方案:1号 替换为 2号 , 2号 替换为 3号

...