print neatly 整齐打印 算法导论

作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4098562.html

题目链接:print neatly 整齐打印 算法导论

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

使用动态规划算法,$dp[i]$表示从第一个单词到第$i$个单词所需要的最小代价。对于每一个单词分别考虑自己单独一行,和前一个单独占据一行$\ldots$ 和前$k$个单词占据一行的情况,其中从$k$到$i$的字符串长度不超过每行最多所能容纳的字符串长度$m$,最后从后向前遍历$dp$数组,计算分别把最后的$k$个单词作为最后一行,且不计算代价的情况下最小的代价。

代码如下:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include  <limits.h>
 7 #define     MAXN 2010
 8 using namespace std;
 9 typedef  long long LL;
10 LL dp[MAXN], w[MAXN][MAXN];
11 int len[MAXN];
12 int n, m;
13 LL solve()
14 {
15     memset(dp, 0, sizeof(dp));
16     memset(w, -1, sizeof(w));
17     for( int i = 0 ; i <= n ; i++ )
18     {
19         for( int j = 0 ; j <= n ; j++ )
20         {
21             w[0][j] = 0;
22         }
23     }
24     for( int i = 1 ; i <= n ; i++ )
25     {
26         for( int j = i ; j <= n ; j++ )
27         {
28             int tmp = m - (j-i) - (len[j]-len[i-1]);
29             if( tmp < 0 )
30             {
31                 break;
32             }
33             w[i][j] = tmp*tmp*tmp;
34         }
35     }
36     dp[0]  = 0;
37     for( int i = 1 ; i <= n ; i++ )
38     {
39         dp[i] = dp[i-1]+w[i][i];
40         for( int j = i-1 ; j >= 0 ; j-- )
41         {
42             if( w[j+1][i] < 0 ) break;
43             dp[i] = min(dp[i], dp[j] + w[j+1][i]);
44         }
45    }
46     LL res = dp[n];
47     for( int i = n ; i >= 0 && w[i][n] >= 0 ; i-- )
48     {
49         res = min(res, dp[i-1]);
50     }
51     return res;
52 }
53 int main(int argc, char *argv[])
54 {
55     while( scanf("%d%d", &n, &m)!=EOF )
56     {
57         len[0] = 0;
58         for( int i = 1 ; i <= n ; i++ )
59         {
60             scanf("%d", &len[i]);
61         }
62         for( int i = 2 ; i <= n ; i++ )
63         {
64             len[i] = len[i-1] + len[i];
65         }
66         printf("%lld\n", solve());
67     }
68 }
69 //input:(n, m, arr[i])
70 //5 5
71 //4 1 1 3 3
72 //5 6
73 //1 3 3 2 3
74 //output:
75 //17
76 //1

时间: 2024-10-01 12:27:23

print neatly 整齐打印 算法导论的相关文章

算法导论之所有排序算法的Python实现

最近一段时间学习了算法导论第二版书的第一部分和第二部分的内容,自己编写了其中排序相关的几乎全部算法,包括冒泡排序(bubble sort).选择排序( selection sort).插入排序(insertion sort).希尔排序(shell sort).归并排序(merge sort).快速排序(quick sort).计数排序(count sort).基数排序(radix sort).桶排序(bucket sort).期望线性时间的第k个顺序统计量选择.最坏情况线性时间的中位数选择,并给

《算法导论》思考题15-2 整齐打印

画外音:没想做到15-2题也是费了一番周折,看来<算法导论>里题都不是白给的 整齐打印问题: 考虑在一个打印机上整齐地打印一段文章的问题.输入的正文是n个长度分别为L1.L2.…….Ln(以字符个数度量)的单词构成的序列.我们希望将这个段落在一些行上整齐地打印出来,每行至多M个字符.“整齐度”的标准如下:如果某一行包含从i到j的单词(i<j),且单词之间只留一个空格,则在行末多余的空格字符个数为 M - (j-i) - (Li+ …… + Lj),它必须是非负值才能让该行容纳这些单词.我

算法导论第十五章动态规划

概述: 1.动态规划是通过组合子问题的解而解决原问题的. 2.动态规划适用于子问题不是独立的情况,也就是各子问题的包含公共的子子问题. 3.动态规划对每个子问题只求解一次,将其结果保存在一张表中. 4.动态规划的设计步骤:a.描述最优解的结构b.递归定义最优解的值c.按自底向上的方式计算最优觖的值d.由计算出的结构构造一个最优解 15.1钢条切割 钢条切割问题:给定定长的钢条和价格表,求切割方案,使得收益最大.如果n英寸的钢条的价格足够大,则不需要切割. 代码如下: //朴素递归求解钢条切割收益

【算法导论】用动态规划解活动选择问题

上一篇讲了贪心算法来解活动选择问题([算法导论]贪心算法之活动选择问题),发现后面有一道练习16.1-1是要用动态规划来解活动选择问题.其实跟之前的矩阵链乘法有些相似,也是考虑分割的活动是哪一个,并用二维数据来记录Sij---最大兼容集合个数,和用另一个二维数据来记录Sij取得最大时的活动分割点k.然后就是考虑边界问题,和使用递归来求动态规划的最优解. 代码注解比较详尽: #include <iostream> #include <algorithm> using namespac

概率分析和随机算法&mdash;&mdash;算法导论(5)

1. 雇用问题 (1) 提出问题     你的老板让你为公司雇用一名程序员,现在有n个人投了简历.你每天会随机的从这n份简历中挑选一份,然后让对应的投简历者过来面试,每次面试都将花费C1.而你的雇用原则是:如果当前面试的程序员比目前的程序员优秀,那么你就辞掉目前的程序员,而花高价C2去聘请面试的这位程序员.你的老板愿意为该策略付费,但是想让你估算一下该费用会是多少. 下面是这种策略的伪代码:               (2) 不科学的算法     因为不管最优秀的面试者出现在哪一个位置,我们总

算法导论之链表

一.概念 (1)数组的线性序是由数组的下标决定的,链表中的顺序是由各对象中的指针所决定的 (2)链表结点结构 node *prev; node *next; int key; (3)链表结点 node *head; node *nil;//哨兵 (4)对链表的操作 LIST-SEARCH(L, k) LIST-INSERT(L, x) LIST-DELETE(L, x) (5)哨兵是个哑对象,可以简化边界条件 二.代码 (1)没有哨兵的情况 [cpp] view plain copy print

算法导论——动态规划

动态规划指的是一个问题可以拆分成多个小的最优子问题,并且这些子问题具有重叠,典型的如斐波那契数列:f(2)=f(1)+f(0),f(3)=f(2)+f(1),f(4)=f(3)+f(2),若使用简单的递归算法求f(4),则f(2)会被计算两次,当计算f(n)时需要计算f(n-1)和f(n-2)而f(n-1)又要计算记一次f(n-2),如此往复时间复杂度为n的指数级别,导致算法效率低下.若能够记录f(2)至f(n-1)的结果,可以保证每一级计算都是O(1)复杂度,整个算法的时间复杂度就能下降至O(

算法导论学习笔记 -(1)

一个ACM若菜,趁着ACM淡季,开始学习算法导论了,经过一年的ACM学习,逐渐的发现,学东西,深入才是王道,以前学习一个算法,总是看懂了就开始做题,到后来才发现很多题目,会算法,却不知道是用这个算法,这就是算法理解的不到位的后果,从今天开始,定下目标: 1.深入系统的学习算法, 2.学会纸上写伪代码,每章的代码自己先在纸上写一遍, 3.每节的学习笔记和算法都要写在博客上. 在博客上记录自己的学习笔记,方便以后温习.   欢迎各路大神路过指正错误之处. 现在,先写写书上的第一个算法,插入排序. 算

算法导论-第24章 Dijkstra算法

Dikstra算法解决的是有向图上单源最短路径问题(无向图可以看成有相反的两条有向边),且要求边的权重都是非负值. 算法导论用了很多引理,性质来证明Dijstra算法的正确性,这里不说了,也表达不明白,只说我理解的过程. 有一个图G( V,E) ,选定一个源点s,维护一个集合Q=V-s,  Q中点有一个d值表示此时从s到该点的已知距离,s.d=0 :初始化都为正无穷,表明不可达.然后对s点所连接的点(设为点集M)进行松弛操作,就是设点m属于M, m.d > s.d+ w(s,m) 则更新 m.d