动态规划之整齐打印

算法描述部分转自:点击打开链接

''' 乍一看以为是空格个数和,如此可以使用“贪心算法”求解,即从第一行开始尽可能多的放置单词,可以证明这个“贪心解”也是最优的,假设有一个最优解从某行开始和“贪心解”不相同,我们可以从这行开始进行构造证明,将下一行的某些单词拿到行尾来使得当前行为“贪心解”,如此“贪心解”的行数必定不大于“最优解”,如此命题得证。

此处的空格立方和,有点类似数学期望的方差,指的是希望所有行的末尾空格看起来尽可能的整齐,即排版感觉上最整齐。举例来说,如果两行的行尾空格总数为5,则空格以2、3(35)分布时立方和最小,1、4分布时立方和(129)相当大。为简单起见,可以对每个单词的长度加1,行的长度也加1,于是就不用考虑单词之间的空格了。

此题的动态规划非常直接,可惜个人刚开始不敢想啊(非常感谢网络友人的无私奉献)!设M(i)为第1个单词到第i个单词的最优排版方案,考虑此方案的最后一行,有以下的递推公式:

M(i) = MIN(M(k)+ left_space(k+1,i)) 1<=k<i

注意left_space 必须非负,即第k+1个单词到第i个单词必须可以放置到一行上,k从i-1开始循环递减,当left_space 为负数时跳出循环。注意当i=n时,即最后一行需要特殊处理,最后一行的left_space 不能加到M(n)上去。''']


此博文仅为记录自己实现的整齐打印:

int countSpace(vector<string> & paper, int i, int j, int M)
{
	--i, --j;
	//if (j == paper.size() - 1)
	//	return 0;
	int sum = M - j + i;
	while (i <= j)
	{
		sum -= paper[i++].size();
	}
//	return sum;
	if (sum >= 0)
		return sum * sum * sum;
	else
		return sum;
}

void printPaper(vector<string> & msg,vector<int>& m,vector<int> &s,int M)
{
	stack<int> loc;
	loc.push(msg.size() - 1);
	int l = s[s.size() - 1];
	while (l >= 0)
	{
		loc.push(l);
		l = s[l + 1];
	}

	int len = loc.top();
	loc.pop();
	int count = 0;
	for (int i = 0; i != msg.size(); ++i)
	{
		count += msg[i].size() + 1;
		cout << msg[i];
		if (i == len)
		{
			cout  << string(M - count + 1,'*') << endl;
			count = 0;
			!loc.empty() ? len = loc.top(), loc.pop() : 1;
		}
		else
			cout << " ";
	}
}

void print(vector<string> & paper,int M)
{
	vector<int> m{ 0 };
	vector<int> s{ -1 };
	for (int i = 1; i <= paper.size();++i)
	{
		int min_val = INT_MAX;
		int min_loc = -1;
		int k = 0;
		while (k < i)
		{
			int space = countSpace(paper, k + 1, i, M);
			if (space >= 0)
			{
				space += m[k];
				space < min_val ? min_val = space, min_loc = k : 1;
			}
			++k;
		}
		if (min_val == INT_MAX)
		{
			cout << "ops" << endl;
			throw runtime_error("ops");
		}
		m.push_back(min_val);
		s.push_back(min_loc - 1);
	}
	
	printPaper(paper,m,s,M);
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章