在几个忙碌的夜晚之后,我的大脑不太好工作,但这需要在昨天修复,所以我要求更刷新的so社区。
我有一串数字。例如:
1,5,7,13,3,3,4,1,8,6,6,6
我需要把这个级数分成三部分,以便所有部分的数字之和尽可能接近。数字的顺序需要保持,所以第一部分必须由第一个X数字组成,第二部分由接下来的Y数字组成,第三部分由剩下的数字组成。
做这个的算法是什么?
(注:实际问题是将不同高度的文本段落排列成三列。段落必须保持顺序(当然),它们不能被分成两半。)
首先,我们需要更好地定义目标:
假设部分和为A1,A2,A3,我们试图最小化|A-A1|+|A-A2|+|A-A3|。A为平均值:A=(A1+A2+A3)/3.
因此,我们试图最小化|A2+A3-2A1|+|A1+A3-2A2|+|A1+A2- 2a3 |
设S表示和(常数):S=A1+A2+A3,因此A3=S-A1-A2。
我们试图最小化:
| A2 + S-A1-A2-2A1 | + | A1 + S-A1-A2-2A2 | + | A1 + A2-2S + 2 A1 + 2 A2 | = | S-3A1 | + | S-3A2 | + | 3 A1 + SA2-2S |
表示这个函数为f,我们可以做两个循环O(n^2)并跟踪最小值:
类似:
for (x=1; x<items; x++)
{
A1= sum(Item[0]..Item[x-1])
for (y=x; y<items; y++)
{
A2= sum(Item[x]..Item[y-1])
calc f, if new minimum found -keep x,y
}
}
find sum and cumulative sum of series.
get a= sum/3
然后在累加和中找到最近的a, 2*a,该累加和将列表分成三个相等的部分。
假设p是段落高度数组;
int len= p.sum()/3; //it is avarage value
int currlen=0;
int templen=0;
int indexes[2];
int j = 0;
for (i=0;i<p.lenght;i++)
{
currlen = currlen + p[i];
if (currlen>len)
{
if ((currlen-len)<(abs((currlen-p[i])-len))
{ //check which one is closer to avarege val
indexes[j++] = i;
len=(p.sum()-currlen)/2 //optional: count new avearege height from remaining lengths
currlen = 0;
}
else
{
indexes[j++] = i-1;
len=(p.sum()-currlen)/2
currlen = p[i];
}
}
if (j>2)
break;
}
将得到第2和第3个序列的起始索引。注意它的伪代码类型:)
我相信这可以用Donald Knuth发明的用于TeX的动态规划断行算法来解决。
根据Aasmund Eldhuset的回答,我之前在SO上回答过这个问题。
换行到X行而不是最大宽度
该算法不依赖于最大行大小,而只是给出最佳切割。
我修改了它来解决你的问题:
L=[1,5,7,13,3,3,4,1,8,6,6,6]
def minragged(words, n=3):
P=2
cumwordwidth = [0]
# cumwordwidth[-1] is the last element
for word in words:
cumwordwidth.append(cumwordwidth[-1] + word)
totalwidth = cumwordwidth[-1] + len(words) - 1 # len(words) - 1 spaces
linewidth = float(totalwidth - (n - 1)) / float(n) # n - 1 line breaks
print "number of words:", len(words)
def cost(i, j):
"""
cost of a line words[i], ..., words[j - 1] (words[i:j])
"""
actuallinewidth = max(j - i - 1, 0) + (cumwordwidth[j] - cumwordwidth[i])
return (linewidth - float(actuallinewidth)) ** P
"""
printing the reasoning and reversing the return list
"""
F={} # Total cost function
for stage in range(n):
print "------------------------------------"
print "stage :",stage
print "------------------------------------"
print "word i to j in line",stage,"ttTotalCost (f(j))"
print "------------------------------------"
if stage==0:
F[stage]=[]
i=0
for j in range(i,len(words)+1):
print "i=",i,"j=",j,"ttt",cost(i,j)
F[stage].append([cost(i,j),0])
elif stage==(n-1):
F[stage]=[[float('inf'),0] for i in range(len(words)+1)]
for i in range(len(words)+1):
j=len(words)
if F[stage-1][i][0]+cost(i,j)<F[stage][j][0]: #calculating min cost (cf f formula)
F[stage][j][0]=F[stage-1][i][0]+cost(i,j)
F[stage][j][1]=i
print "i=",i,"j=",j,"ttt",F[stage][j][0]
else:
F[stage]=[[float('inf'),0] for i in range(len(words)+1)]
for i in range(len(words)+1):
for j in range(i,len(words)+1):
if F[stage-1][i][0]+cost(i,j)<F[stage][j][0]:
F[stage][j][0]=F[stage-1][i][0]+cost(i,j)
F[stage][j][1]=i
print "i=",i,"j=",j,"ttt",F[stage][j][0]
print 'reversing list'
print "------------------------------------"
listWords=[]
a=len(words)
for k in xrange(n-1,0,-1):#reverse loop from n-1 to 1
listWords.append(words[F[k][a][1]:a])
a=F[k][a][1]
listWords.append(words[0:a])
listWords.reverse()
for line in listWords:
print line, 'tt',sum(line)
return listWords
我得到的结果是:
[1, 5, 7, 13] 26
[3, 3, 4, 1, 8] 19
[6, 6, 6] 18
[[1, 5, 7, 13], [3, 3, 4, 1, 8], [6, 6, 6]]
希望有所帮助