问题描述如下,232页:

 

 考虑整齐打印问题,即在打印机上用等宽字符打印一段文本。输入文本为n个单词的序列,单词长度分别是l 1,l 2,l 3...l n个字符。我们希望将此段文本整齐打印在若干行上,每行最多M个字符。“整齐”的标准是这样的。如果某行包括含第i到第j(i<=j)个单词,且单词间隔为一个空格符,则行尾的额外空格符数量为M-j+i-∑l k(i<=k<=j),此值必须为非负的,否则一行内无法容纳这些单词。我们希望能最小化所有行的(除最后一行外)额外空格数的立方之和。设计一个 动态规划算法,在打印机上整齐打印一段n个单词的文本。分析算法的时间和空间复杂性。

 

解决思路:
 由问题描述可知道,我们需要做的就是n个单词分成若干份,这与矩阵连乘问题有相似之处。行尾的额外空格符数量M-j+i-∑l k(i<=k<=j)可以看成 M-(j-i)-∑lk 其中j-i是表示该行各个单词之间相隔的空格数量。我们设lc[i,j]是第i个单词到第j个单词的额外空格符数量的立方,即是(M-j+i-∑l k) 3(i<=k<=j),则有已下公式:
lc[i,j]=∞,当M-j+i-∑l k<0 (i<=k<=j);
lc[i,j]=0,当M-j+i-∑l k>=0 (i<=k<=j),且j==n;
lc[i,j]=(M-j+i-∑lk)3,当当M-j+i-∑l k>=0 (i<=k<=j),且j!=n。

 

公式解析: 
当M-j+i-∑l k<0时,说明第i个单词到第j个单词被划作成一行的时候,各单词以空格相隔后,其字符数量已经超出了M,也就是说不应该把第i个单词到第j个单词作为一行,因此设其lc值为无穷大。若当j==n时,证明第i个单词到第j个单词所划分成的一行是最后一行,所以根据题目意思,其lc值为0。除去以上两种情况,lc值按照额外空格数的立方进行计算。

 

根据动态规划的思想,必须找到问题的最优子结构性质,我们将第i,i+1...j个单词一字排开,每次选择一个位置k将一行划分出来,假设我们从这堆单词后面开始,也就是选定第k个单词到第j个单词作为一行,使得其在完成结合往后的选择然后所有行的(除最后一行外)额外空格数的立方之和最小,剩下子问题,需要对第i,i+1...k-1个单词选择一个最优位置进行划分。于是我们设c[j]是从第i个单词开始到第j个单词结束的序列的在划分成若干行后各行额外空格数的立方之和。(可以看到我们并没有设成是c[i,j],那是因为自始至终,原问题和子问题中的单词序列都是以i开始的,当然,当我们在实际实现的时候,尤其是在使用自底向上的方法的时候(也就是首先计算出原问题最小的子问题,然后再计算更大的子问题直到原问题的方法),我们要解决的是第1,2,3...n个单词的序列,当然要从计算c[1]开始。)
c[j]的计算公式如下:
c[j]=0,当j=0;
c[j]=min {c[k-1] + lc[k,j] }(1<=k<=j),当j>0;

 

显然,当j=时,即是输入的序列为空,那么问题将没意义,故设为0;当j>0时,选择第k...j个单词组成一行,给k值是从1到j之间寻找,类似于矩阵连成找k值的情况,此时子问题第1...k-1个单词的最优值加上选择第k...j个单词组成一行的额外空格符数量的立方,便组成了原问题的解。于是,我们便递归的定义了该问题的最优解。

 

以下是问题的实现

 
  
  1. #include <iostream> 
  2. #include <fstream> 
  3. using namespace std; 
  4.  
  5. #define M 85 //每行最多M个字符
  6. #define N 49  //N个单词的输入序列
  7. #define INFINITE (M*M*M) //定义无穷大为M的立方,各个lc永远无法取到该值 
  8. int l[N+1];//各个单词的字符数目 
  9. int c[N+1];//问题的解 
  10. int r[N+1];//存放最优解,k值 
  11. unsigned int lc[N+1][N+1];//lc[i][j]是第i个单词到第j个单词的额外空格符数量的立方 
  12.  
  13. void print_neatly();//求解整齐打印 
  14. void cal_lc();//计算lc值 
  15. int give_line(int j);//打印最优解 
  16.  
  17. void main(int argc, char **argv){ 
  18.     cout<<sizeof(int)<<endl; 
  19.     ifstream infile; 
  20.     infile.open("input.txt");//读入一个有各点坐标的文档 
  21.     if (!infile) 
  22.     { 
  23.         cout<<"error!"<<endl; 
  24.     } 
  25.     int i=1; 
  26.     while (infile>>l[i]) 
  27.     { 
  28.         i++; 
  29.     } 
  30.     for (int j=1;j<=N;j++) 
  31.     { 
  32.         cout<<l[j]<<" "
  33.         if (j%10==0) 
  34.         { 
  35.             cout<<endl; 
  36.         } 
  37.     } 
  38.     cout<<endl; 
  39.     print_neatly(); 
  40.     give_line(N); 
  41.     cout<<"所有行的(除最后一行外)额外空格数的立方之和为:"<<c[N]<<endl; 
  42.  
  43. void print_neatly(){ 
  44.     c[0] = 0; 
  45.     cal_lc(); 
  46.     for (int j=1;j<=N;j++) 
  47.     { 
  48.         c[j]= c[0]+lc[1][j];//先取c[j]的初值是k=1时的值 
  49.         r[j]=1; 
  50.         for (int k=1;k<=j;k++) 
  51.         { 
  52.             int q=c[k-1]+lc[k][j]; 
  53.             if (q<c[j]) 
  54.             { 
  55.                  c[j]= q; 
  56.                  r[j]=k; 
  57.             } 
  58.         } 
  59.     } 
  60.  
  61. void cal_lc(){//计算lc值 
  62.     for (int i=1;i<=N;i++) 
  63.     { 
  64.         for (int j=i;j<=N;j++) 
  65.         { 
  66.             int words_length=0; 
  67.             for (int k=i;k<=j;k++) 
  68.             { 
  69.                 words_length += l[k]; 
  70.             } 
  71.             int extra=M-j+i-words_length; 
  72.             if (extra<0) 
  73.             { 
  74.                 lc[i][j] = INFINITE; 
  75.             }else if(j==N && extra>=0){ 
  76.                     lc[i][j]=0; 
  77.             }else
  78.                 lc[i][j]= extra*extra*extra; 
  79.             } 
  80.         } 
  81.     } 
  82.  
  83. int give_line(int j){ 
  84.     int i=r[j],k;//k是划分后的行数 
  85.     if (i==1) 
  86.     { 
  87.         k=1; 
  88.     }else
  89.         k=give_line(i-1)+1; 
  90.     } 
  91.     cout<<"行号\t起始单词\t结束单词"<<endl; 
  92.     cout<<k<<"\t"<<i<<"\t\t"<<j<<endl; 
  93.     return k; 

输入文件input.txt如下:

 

4 2 4 7 2 6 2 2 3 11

4 7 9 4 2 3 10 8 2 13

3 7 11 4 2 5 1 7 8 13

13 8 12 8 7 4 6 2 3 7 11

3 13 3 7 2 11 10 8

注意以上数据是根据某英文书籍某段话的统计,包括代码中的M值,N值。

运行结果:

整齐打印的结果是:

Just as each project is unique so is its environment This chapter discusses someXXXXX

of the components involved in understanding the project environment such as using aXX

systems approach understanding organizations managing stakeholders matching productXX

life cycles to the project environment and understanding the context of informationXX

technology procjects

第一行额外空格数是5,第二到第四的额外空格数为2,所以53+23+23+23=149。