zoukankan      html  css  js  c++  java
  • 整齐打印

    问题:

          考虑整齐打印问题,即在打印机上用等宽字符打印一段文本。输入文本为n个单词的序列,单词长度分别为l1,l2,……,ln个字符。要求将此段文本整齐打印在若干行上,每行最多M个字符。若每行包含第i到底j(i<=j)个单词,且单词间隔为一个空格符,则行尾的额外空格符数量为M-j+i-(li+……+lj),此值必须为非负的,否则一行内无法容纳这些单词。要求能最小化所有行(除最后一行)的额外空格数。

    解决思路(转载):

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

     
    lc[i,j]=∞,当M-j+i-∑lk<0 (i<=k<=j);
    lc[i,j]=0,当M-j+i-∑lk>=0 (i<=k<=j),且j==n;
    lc[i,j]=(M-j+i-∑lk)3,当当M-j+i-∑lk>=0 (i<=k<=j),且j!=n。
     
    公式解析: 

          当M-j+i-∑lk<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个单词组成一行的额外空格符数量的立方,便组成了原问题的解。于是,我们便递归的定义了该问题的最优解。

     
     
    // zhengqidayin.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include<iostream>
    using namespace std;
    #include <time.h> 
    
    
    #define M 50   //每行最多M个字符
    #define N 50   //单词个数
    #define Inf 65536 //无穷大
    int lc[N + 1][N + 1];       //第i个单词到第J个单词空格数的立方
    int l[N + 1];             //每个单词的长度
    int c[N + 1];             //存放第1个单词到第i个单词最优时的立方空格数和
    int r[N + 1];             //用于存放最优解
    
    int RANDOM(int p, int r)         //用于产生随机数
    {
    	srand((unsigned)time(NULL));
    	return (rand() % (r - p + 1)) + p;
    }
    
    void cal_lc()                   //计算所有当第i个单词和第j个单词放在同一行上时额外的空格数
    {
    	for (int i = 1; i <= N; i++)
    	{
    		for (int j = i; j <= N; j++)
    		{
    			int word_length = 0;
    			for (int k = i; k <= j; k++)
    				word_length += l[k];
    			int blank = M - j + i - word_length;
    			if (blank < 0)  //说明第i到第j个单词不能放在同一行上
    				lc[i][j] = Inf;
    			else if (j == N)  //说明为最后一行,不计入空格数
    				lc[i][j] = 0;
    			else if (blank >= 0)
    				lc[i][j] = blank*blank*blank;
    		}
    	}
    }
    
    void result()
    {
    	c[0] = 0;
    	cal_lc();
    	for (int j = 1; j <= N; j++)
    	{
    		c[j] = c[0] + lc[1][j]; 
    		r[j] = 1;
    		for (int k = 1; k <= j; k++)    //此循环求递归式min
    		{
    			int q = c[k - 1] + lc[k][j]; 
    			if (q<c[j])
    			{
    				c[j] = q;
    				r[j] = k;            //某行以k为起点,j为终点
    			}
    		}
    	}
    }
    
    int print(int j)
    {
    	int i = r[j];   //得到起点,j为终点
    	int num;
    	if (i == 1) num = 1;
    	else
    		num = print(i - 1) + 1;             //i-1为上一行的末尾
    	cout << num << '	' << i << "		"<< j << endl;
    	return num;
    }
    
    int main()
    {
    	for (int i = 1; i <= N; i++)
    		l[i] = RANDOM(3, 3+i%11);
    	cout << "给定各单词长度:" << endl;
    	for (int i = 1,k=0; i <= N; i++)
    	{
    		k++;
    		cout << l[i] << '	';
    		if (k % 5 == 0)cout << endl;
    	}
    	cout << endl<<"行号	起始单词	结束单词" << endl; 
    	result();
    	print(N);
    	while (1);
        return 0;
    }
    

      

  • 相关阅读:
    归并、希尔排序
    堆排序
    [模板] 最小树形图/朱刘算法
    [模板] 常系数线性递推
    [模板] Kruskal算法 && 克鲁斯卡尔重构树
    [模板] 斯特林数,性质以及求法
    这几天想干什么
    奇怪的 Markdown / LaTeX 笔记
    [模板] 各种莫队
    [模板] 2-SAT 问题
  • 原文地址:https://www.cnblogs.com/linear/p/6649189.html
Copyright © 2011-2022 走看看