动态规划之整齐打印

考虑在一个打印机上整齐地打印一段文章的问题。输入的正文是n个长度分别为L1、L2、……、Ln(以字符个数度量)的单词构成的序列。我们希望将这个段落在一些行上整齐地打印出来,每行至多M个字符。“整齐度”的标准如下:如果某一行包含从i到j的单词(i<j),且单词之间只留一个空格,则在行末多余的空格字符个数为 M - (j-i) - (Li+ …… + Lj),它必须是非负值才能让该行容纳这些单词。我们希望所有行(除最后一行)的行末多余空格字符个数的立方和最小。请给出一个动态规划的算法,来在打印机整齐地打印一段又n个单词的文章。分析所给算法的执行时间和空间需求。

 

乍一看以为是空格个数和,如此可以使用“贪心算法”求解,即从第一行开始尽可能多的放置单词,可以证明这个“贪心解”也是最优的,假设有一个最优解从某行开始和“贪心解”不相同,我们可以从这行开始进行构造证明,将下一行的某些单词拿到行尾来使得当前行为“贪心解”,如此“贪心解”的行数必定不大于“最优解”,如此命题得证。

 

此处的空格立方和,有点类似数学期望的方差,指的是希望所有行的末尾空格看起来尽可能的整齐,即排版感觉上最整齐。举例来说,如果两行的行尾空格总数为5,则空格以2、3(35)分布时立方和最小,1、4分布时立方和(129)相当大。为简单起见,可以对每个单词的长度加1,行的长度也加1,于是就不用考虑单词之间的空格了。

此题的动态规划非常直接,可惜个人刚开始不敢想啊(非常感谢网络友人的无私奉献)!设M(i)为第1个单词到第i个单词的最优排版方案,考虑此方案的最后一行,有以下的递推公式:

M(i) = MIN(M(k)+ left_space(k+1,i)) 1<=k<i

注意left_space 必须非负,即第k+1个单词到第i个单词必须可以放置到一行上,k从i-1开始循环递减,当left_space 为负数时跳出循环。注意当i=n时,即最后一行需要特殊处理,最后一行的left_space 不能加到M(n)上去。以下是代码实现:

void output_neatly(int *words, int pos, int *P, int *M)
{
    int i = 0, sum = 0;
    if (P[pos] > 0) {
        output_neatly(words, P[pos], P, M);
    }
    for (i = P[pos];i < pos;i++) {
        sum += words[i];
    }
    printf("%4d = ", sum);
    for (i = P[pos];i < pos;i++) {
        printf("%4d ", words[i]);
    }
    printf("\n");
}

void print_neatly(int *words, int n, int L)
{
    int i = 0, j = 0;
    int left = L, line_space = 0;;
    unsigned int *M = malloc(sizeof(int) * (n + 1));
    int *P = malloc(sizeof(int) * (n + 1));

    M[0] = 0;
    for (i = 1;i <= n;i++) {
        M[i] = -1;
        left = L;
        for (j = i - 1;j >= 0;j--) {
            left -= words[j];
            if (left < 0)
                break;
            if (i == n)
                line_space = 0; /* last line is ignored when calc spaces */
            else
                line_space = left * left * left;
            if (M[i] > M[j] + line_space) {
                M[i] = M[j] + line_space;
                P[i] = j;
            }
        }
    }

    printf("\nThe min space is %d, and the neatly printing is below:\n", M[n]);
    output_neatly(words, n, P, M);

    free(M);
    free(P);
}

代码中使用了数组P保存最优解情况下上一行的单词序号,如此可以方便的输出最优解的结构,不仅仅是得到一个最小立方和。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值