UVa 709 - Formatting Text

题目:给你一段文字,让你对文字排版,每行的最大长度有限制L,有一个对于排版格式的权值公式:

                 P = sum((单词间空格-1)*(单词间空格-1))

            求出P最小的一种排版,F相同时输出前面的空格最少的。

分析:DP。看见题目第一想到的就是利用dp,设F(i,j)是从第i个单词开始排版,到第j个单词结束的最小P

            可以得到状态转移方程: 

                  F(i,j)= min(F(i,k-1)+min_line(k,j)),i< k <= j 且 k到j的单词总长+j-k坏人空格不超过L

            其中,min_line(k,j)是以k开始,j结束的一行的最小的P:

                  可以证明当每一行的单词确定后,平均分配中间的间隔,可以使得min_line最小。

            最后,要注意计算的方向是从后向前,因为要保证前面的空格最少。

                  对于输出,保留上一行结束位置,每行单独计算即可。

            (按顺序计算,后计算的优先级高,想一想求最短路时,如果保存路径使得前面路段最短,要逆向运算)

注意:数据的读入格式有点恶心。

// a+b = c, aa + bb = aa + (c-a)(c-a) = cc + 2aa - 2ac,最值在 2a = c 
#include 
#include 
#include 
#include 

typedef struct dnode
{
	char word[85];
	int  length;
}data;
data D[10005];

int sumL[10005];
int F[10005];
int E[10005];

void output( int i, int width, int max )
{
	if ( i < max ) {
		if ( E[i] > i+1 ) {
			//计算每行中具体的空格分布 
			int sblack = width-sumL[E[i]-1]+sumL[i-1];
			int eblack = sblack/(E[i]-1-i);
			int nshort = (eblack+1)*(E[i]-1-i)-sblack;
			int count = 0;
			printf("%s",D[i].word);
			for ( int j = i+1 ; j < E[i] ; ++ j ) {
				for ( int k = 0 ; k < eblack ; ++ k )
					printf(" ");
				if ( ++ count > nshort ) printf(" ");
				printf("%s",D[j].word);
			}printf("\n");
		}else printf("%s\n",D[i].word);//自己独立一行 
		output( E[i], width, max );
	}
}

int main()
{
	int width,temp;
	while ( scanf("%d",&width) && width ) {
		getchar();
		int count = 1;
		while ( (temp = getchar()) != '\n' ) {
			do {
				ungetc( temp, stdin );
				scanf("%s",D[count].word);
				D[count].length = strlen(D[count].word);
				count ++;
			}while ( (temp = getchar()) != '\n' );
		}
		
		sumL[0] = 0;
		for ( int i = 1 ; i < count ; ++ i )
			sumL[i] = sumL[i-1]+D[i].length;
		
		F[count] = 0;
		for ( int i = count-1 ; i > 0 ; -- i ) {
			F[i] = F[i+1] + 500;
			E[i] = i+1;
			for ( int j = i+1 ; j < count && sumL[j]-sumL[i-1]+j-i <= width ; ++ j ) {
				//计算每行中具体的空格分布
				int sblack = width-sumL[j]+sumL[i-1];
				int eblack = sblack/(j-i);
				int nshort = (eblack+1)*(j-i)-sblack;
				int add = (j-i-nshort)*eblack*eblack+nshort*(eblack-1)*(eblack-1);//本行代价 
				if ( F[i] >= F[j+1] + add ) {
					F[i] = F[j+1] + add;
					E[i] = j+1;
				}
			}
		}
		
		output( 1, width, count );
		printf("\n");
	}
	return 0;
}

附上两组比较恶心的数据:

input:
25
x x x x x  xxxxxxxxxxxxxx
x            xxxxxxxxxxxx
xxxxxxxxxxx             x
xxxxxxxxxxxxxx x x x x  x

5
La la la

output:
x x x x x  xxxxxxxxxxxxxx
x            xxxxxxxxxxxx
xxxxxxxxxxx             x
xxxxxxxxxxxxxx x x x x  x

La la
la


你可能感兴趣的:(解题报告,动态规划(DP),APOC-UVa)