算法描述部分转自:点击打开链接
''' 乍一看以为是空格个数和,如此可以使用“贪心算法”求解,即从第一行开始尽可能多的放置单词,可以证明这个“贪心解”也是最优的,假设有一个最优解从某行开始和“贪心解”不相同,我们可以从这行开始进行构造证明,将下一行的某些单词拿到行尾来使得当前行为“贪心解”,如此“贪心解”的行数必定不大于“最优解”,如此命题得证。
此处的空格立方和,有点类似数学期望的方差,指的是希望所有行的末尾空格看起来尽可能的整齐,即排版感觉上最整齐。举例来说,如果两行的行尾空格总数为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);
}