uva709 - Formatting Text(递推)

题目:uva709 - Formatting Text(递推)

题目大意:给你一段文章,里面有很多的单词。要求你排版,按照题目给定的长度,并且要求每一行要以单词开头,单词结束。如果一行只有一个单词的话,就放在最开头。并且根据badness最小来排版。

解题思路:这题知道是DP就不难想到状态,但是这题要考虑很多的细节问题,例如:一行只要一个单词的话,就要直接回车,不能再输出空格,因为这个PE了。而且还有单独一行一个单词,如果这个单词的长度小于要求的长度,那么badness = 500;并且这里要求的是前面的单词之间的间隙比较小,那么就需要从后面往前面递推,还需要考虑这个单词是否放单独的一行最优,还是后面接个单词比较优,总之好麻烦,一不当心就会错。

状态转移:dp【i】【j】 (第i个单词放在它所在的那一行的j位置(起始处)) dp【i】【j - l【i】 + k】 = dp【i + 1】【j】 + (k - 1) * (k - 1); k是空格数,注意j - l【i】 + k要在0 -- L - l【i】之间,并且这里的k当j == 0的时候可以等于0,相当于这两个单词在两行。其余的时候就至少要等于1.然后还要单独的考虑自己一行的情况。

代码:

#include <cstdio>
#include <cstring>

const int N = 10005;
const int M = 85;
const int INF = 0x3f3f3f3f;

int L, n;
int dp[N][M];
int p[N][M];//路径
char str[N];
char word[N][M];
int l[N];//单词长度

int Min (const int a, const int b) { return a < b ? a: b; }

void handle () {处理输入

	int j = 0;
	bool flag = 0;
	for (int i = 0; i <= strlen (str); i++) {

		if (str[i] != ' ' && str[i] != '\0') {
			word[n][j++] = str[i];
			flag = 1;
		} else {
			if (flag) {
				word[n][j] = '\0';
				l[n++] = j;
				j = 0;
				flag = 0;
			}
		}
	}
}

void printf_ans (int x, int y) {//输出路径

	if (x == n + 1)
		return;
	if (!p[x][y] && !y) {//单独一行不要输出空格直接回车
		printf ("%s", word[x - 1]);
	} else {

		printf ("%s", word[x - 1]);
		if (x != n) {
			for (int i = y + l[x - 1]; i < p[x][y]; i++)
				printf (" ");
		}
	}

	if (!p[x][y] || x == n)
		printf ("\n");
	printf_ans(x + 1, p[x][y]);
}

int main () {

	int tmp;
	while (scanf ("%d%*c", &L), L) {

		n = 0;
		while (gets(str) && str[0] != '\0') {

			handle();
		}
		//init
		for (int i = 0; i <= n; i++)
			for (int j = 0; j <= L; j++) {
				dp[i][j] = INF;
				p[i][j] = L + 1;
			}
		dp[n][0] = 500;
		p[n][0] = 0;
		dp[n][L - l[n - 1]] = 0;

		for (int i = n - 1; i >= 1; i--) {
			for (int j = 0; j <= L - l[i]; j++) {

				if (dp[i + 1][j] == INF)
					continue;
				if (!j) {

					if (dp[i + 1][j] <= dp[i][L - l[i - 1]]) {//两个单词在两行的情况
						dp[i][L - l[i - 1]] = dp[i + 1][j];
						p[i][L - l[i - 1]] = j;
					}
					tmp = (l[i - 1] == L) ? 0: 500;
					if (dp[i + 1][j] + tmp <= dp[i][0]) {//单独一行
						dp[i][0] = dp[i + 1][j] + tmp;
						p[i][0] = j;
					}

				} else {

					for (int k = 0; k < j - l[i - 1]; k++) {//中间有空格的情况

						tmp = j - l[i - 1] - k - 1;
						if (dp[i + 1][j] + tmp * tmp < dp[i][k]) {

							dp[i][k] = dp[i + 1][j] + tmp * tmp;
							p[i][k] = j;
						} else if (dp[i + 1][j] + tmp * tmp == dp[i][k]) {

							if (p[i][k] > k)//如果后面可以接单词的话,那么就不要单独一行
								p[i][k] = Min (p[i][k], j);//使得前面的空格尽量少
							else
								p[i][k] = j;
						}
					}
				}
			}
		}

		printf_ans(1, 0);
		printf ("\n");
	}
	return 0;
}
时间: 2024-11-14 11:18:42

uva709 - Formatting Text(递推)的相关文章

递推DP HDOJ 5459 Jesus Is Here

题目传送门 题意:简单来说就是sn = sn-1 + sn-2递推而来,求其中所有c字符的:∑i<j:sn[i..i+2]=sn[j..j+2]=‘‘cff"(j−i) mod 530600414 分析:一开始觉得很难下手,类似于斐波那契数列,最后的数字会很大,不能正常求解.想到试试打表找规律,结果并没有找到什么规律...最后也没想出什么来.赛后才恍然大悟,这是递推题,拿来别人的思路: 串长度len,串中字符c的个数num,串中所有字符c的位置之和sum,串中所有字符c之间的距离之和ans

hdu(1078)——FatMouse and Cheese(递推型动归)

题意: 现在有n*n的方块,然后每个方块中都有一个值,代表这个方块中含有奶酪的数量. 现在那个老鼠站在(0,0)点,并且它每次最多走k步,然后每次走到一个点,它都能获得那个点上含有的值,但是要保证它下次走到的点含有的奶酪数必须多于它现在在的那个位置上的奶酪数量. 思路: 这道题也是一道递推性动归,和poj 1088 滑雪那题的区别就是这题最多可以走k步. 这道题让我更加深刻的理解了递推性动归. #include<stdio.h> #include<string.h> #includ

hdu 1165 Eddy&amp;#39;s research II(数学题,递推)

// Eddy 继续 Problem Description As is known, Ackermann function plays an important role in the sphere of theoretical computer science. However, in the other hand, the dramatic fast increasing pace of the function caused the value of Ackermann function

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

hdu 1267 递推

下沙的沙子有几粒? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4326    Accepted Submission(s): 2268 Problem Description 2005年11月份,我们学校参加了ACM/ICPC 亚洲赛区成都站的比赛,在这里,我们获得了历史性的突破,尽管只是一枚铜牌,但获奖那一刻的激动,也许将永远铭刻

hdu 2067(递推或卡特兰数【待补充】)

//解法一:递推#include<iostream> using namespace std; long long d[36][36]; int main() { for(int i=1;i<=35;i++) { d[0][i]=1; } for(int i=1;i<=35;i++) for(int j=i;j<=35;j++) { if(i==j) d[i][j]=d[i-1][j]; else d[i][j]=d[i-1][j]+d[i][j-1]; } int n; i

NPU 2015年陕西省程序设计竞赛网络预赛(正式赛)F题 和谐的比赛(递推 ||卡特兰数(转化成01字符串))

Description 今天西工大举办了一场比赛总共有m+n人,但是有m人比较懒没带电脑,另外的n个人带了电脑.不幸的是,今天机房的电脑全坏了只能用带的电脑,一台电脑最多两人公用,确保n>=m.但是大家来的时间不同,随机次序来机房,带电脑的人直接准备比赛而没带电脑的人需要向带电脑并还没和别人公用的人求助(当然会答应).但是,如果不存在带电脑并还没和别人公用的人,那他就要等了,等是很让人头疼的,这就不和谐了,当然假如没有这样的情况发生比赛是很和谐的. Input 输入多组数据,每组数据只有一行m(

矩阵经典题目七:Warcraft III 守望者的烦恼(矩阵加速递推)

https://www.vijos.org/p/1067 很容易推出递推式f[n] = f[n-1]+f[n-2]+......+f[n-k]. 构造矩阵的方法:构造一个k*k的矩阵,其中右上角的(k-1)*(k-1)的矩阵是单位矩阵,第k行的每个数分别对应f[n-1],f[n-2],,f[n-k]的系数.然后构造一个k*1的矩阵,它的第i行代表f[i],是经过直接递推得到的.设ans[][]是第一个矩阵的n-k次幂乘上第二个矩阵,f[n]就是ans[k][1]. 注意:用__int64 #in

uva 1478 - Delta Wave(递推+大数+卡特兰数+组合数学)

题目链接:uva 1478 - Delta Wave 题目大意:对于每个位置来说,可以向上,水平,向下,坐标不能位负,每次上下移动最多为1, 给定n问说有多少种不同的图.结果对10100取模. 解题思路:因为最后都要落回y=0的位置,所以上升的次数和下降的次数是相同的,并且上升下降的关系满足出栈入栈的关系.即卡特兰数. 所以每次枚举i,表示有i个上升,i个下降,用组合数学枚举出位置,然后累加求和. C(2?in)?f(i)=C(2?i?2n)?f(i?1)?(n?2?i+1)?(n?2?i+2)