OJ1093-Formatting Text

原题目:http://poj.org/problem?id=1093

 

//题目分析:题目大致意思是让你对一段文章进行调整,使其可以达到最佳的效果。题目中为了了衡量好与坏,特设定了一个bad数组,每两个单词之间如果有n个空隙,那么bad值就为(n-1)^2,现在要求调整后,bad 最小,并把最优的方案输出

//算法分析:
单词全部读取以后,变成完整的一段(不管原来有几行),按照指定的宽度width重新排版。插入的空格数,要按不良度(badness)进行评估。在各种可行的方案中,要使用不良度最小的排版方案。因为排版时,一行只剩下一个单词的情况,肯定是在最后一行,所以从最后一个单词开始排版

dp实现
void dp(int num); 形参num是该段落的单词总数
反向dp,即倒着推。设从第i(i>=0&&i<=num)个单词起的line[i]个单词作为第一行(dp的状态表示),从第line[i]+i个单词起的line[line[i]+i]个单词作为第二行
设变量j表示从第i个单词起的第j个单词(1<=j&&j<=num-i),并且第j个单词能够排版到该行中(控制该行字符和空格的长度),则有递推式 line[i]={j | min(badness(i,j))} (0<=i<num,i<=j<=num-i,且第j个单词能够排版到该行中)
badness(i,j)=badness[i+j]+score 式中 score 是当前行的badness

输出排版结果
根据数组line输出即可。设变量k表示每行的行首单词编号,则第一行k=0;第二行,k=line[0];第三行,k+=line[1],依次类推
空格的分配:设当前行单词字符的总长度是total,则每个间隙中的最少空格数q是:
q=(width-total)/(j-1);
这样,还有r个空格未安排
r=(width-total)%(j-1);
需要r个间隙中再多一个空格,放在该行的后面,就满足了题目要求的优化方法


#include<stdio.h>
#include<string.h>

#define MAXN 1000
#define MAXM 60
const int INF = 999999999;
char word[MAXN][MAXM];  //一个段落,段落有多行
int width;   //排版宽度
int len[MAXN];  //每个单词的长度
int line[MAXN]; //用于dp的数组,每个单词到行末的单词个数

//dp的实现,形参num是单词总数
void dp(int num)
{
    int i,j;
    int badness[MAXN]; //用于dp的数组,相应于数组line的每个单词的不良度(badness)
    badness[num]=0;
    for(i=num-1;i>=0;i--) //从最后一个单词起的每个单词
    {   line[i]=1;
        j=1;  //单词i本身
        int total=0;  //该行字符的长度
        int min=INF; //最小不良度
        while(j+i-1<num&&total+len[i+j-1]+j-1<=width) //将单词i放在行首,在一行内能够排版的单词数
        {   total+=len[i+j-1]; //这里其实就是一个dp递推的式子,它这里一开始是没有算出来的,需要从后面的递推中得到
            int score=badness[i+j];
            if(j==1) score+=500;  //只能放一个单词的不良度
            else
            {  int q=(width-total)/(j-1);  //每个间隙中的最少空格数
               int r=(width-total)%(j-1); //r个间隙中,每个间隙再多一个空格
               score+=r*q*q+(j-1-r)*(q-1)*(q-1);
            }
            if(score<=min) //最小不良度
            {  min=score;
               line[i]=j;   //该行到行末的单词个数
            }
            j++;  //再放一个单词
        }
        badness[i]=min; //该单词的不良度
    }
}

int main()
{
    int i,j;
    char ch[500];
    while(scanf("%d\n",&width)&&width)  //这里的scanf()中有 \n 可以处理回车
    {   int word_num=0;
        while(gets(ch)&&strlen(ch))  //读取一行,且不是空行
        {   int pos=0;
            while(ch[pos])  //不是行末
            {   sscanf(ch+pos,"%s",word[word_num]);
                len[word_num]=strlen(word[word_num]);  //存放该单词的长度
                pos+=strlen(word[word_num++]);
                while(ch[pos]&&ch[pos]==' ') pos++;
            }
        }
        dp(word_num);
        int k=0; //每行第一个单词的序号
        while(k<word_num)
        {   int total=0;  //该行中字符的长度
            for(i=k;i<k+line[k];i++)
               total+=len[i];
            if(line[k]==1)
            {  printf("%s\n",word[k]);
               k++;
               continue;
            }
            int q=(width-total)/(line[k]-1);  //每个间隙中的最少空格数
            int r=(width-total)%(line[k]-1);  //r个间隙中,每个间隙再多一个空格,放在该行的后面
            for(i=k;i<k+line[k]-1;i++) //输出该行的前p[k]-1 个单词
            {  printf("%s",word[i]);
               int space = q+(i>=k+line[k]-1-r);
               for(j=0;j<space;j++)
                   putchar(' ');
     }
               printf("%s\n",word[line[k]+k-1]); //输出该行的最后一个单词
               k+=line[k]; //下一行的行首单词的序号
            }
            printf("\n");
       }
 
return 0;
}

转载于http://blog.163.com/zjut_nizhenyang/blog/static/169570029201010572252847/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值