原题目: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/