動態規劃之整齊打印

算法描述部分轉自:點擊打開鏈接

''' 乍一看以爲是空格個數和,如此可以使用“貪心算法”求解,即從第一行開始儘可能多的放置單詞,可以證明這個“貪心解”也是最優的,假設有一個最優解從某行開始和“貪心解”不相同,我們可以從這行開始進行構造證明,將下一行的某些單詞拿到行尾來使得當前行爲“貪心解”,如此“貪心解”的行數必定不大於“最優解”,如此命題得證。

此處的空格立方和,有點類似數學期望的方差,指的是希望所有行的末尾空格看起來儘可能的整齊,即排版感覺上最整齊。舉例來說,如果兩行的行尾空格總數爲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);
}



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