画圆的沙滩

亦简亦美

线性序列的最优化

这与编程之美的4.5节磁带文件的存放优化有关。原题要求使得所有的平均访问长度最短,而存放序列是线性的。事实上,这是一个排序问题。

我们从排序问题本身来看。假设我们已经有一个序列:58976231。我们的目标是使得由这些数字构成的值最小。这是一个显然的排序问题。不过,这里换一个角度来看待它。我们采用局部优化的方式来解决这个问题。首先,对于任意相邻的两个数字ab,如果a < b,显然就有 ab < ba。为了使问题能更一般化一些,假设无法进行简单的a,b比较, 我们如何再进行排序呢?比如说在原题的情况下,我们就无法简单对两个文件进行比较。换一个思路,考虑这两个文件构成的序列,我们是可以评价它的存取效率的。即,可以通过直接判断ab和ba的大小关系来评判a,b的大小关系。亦即:a < b <==> ab < ba。这样,我们将一个不同的问题转化成为了原题的子问题。再回顾排序的过程,以冒泡排序为例,这其实就是一个不断优化现有解,从而最终达到最优解的过程。

回到原题。我们从任意某序列开始,假设文件i, j相邻,评估序列ij和ji的成本,有:ij: L*Pi + (L + Li)*Pj = L*(Pi + Pj) + Li*Pj 和 ji: L*Pj + (L + Lj)*Pi = L*(Pi + Pj) + Lj*Pi。所以我们有比较函数:

bool compare(const File& fa, const File& fb) {
return fa.len*fb.p < fb.len*fa.p;
}

再应用一个常用的排序函数,我们就获得了最优解。参考下题:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=113&page=show_problem&problem=1846

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=113&page=show_problem&problem=967

参考解题代码:

bool compare(const string& sa, const string& sb) {
return sa + sb > sb + sa;
}

int main() {
vector
<string> vec;
int n;
while (cin>>n) {
if (!n) break;
vec.clear();
while (n--) *back_inserter(vec) = *istream_iterator<string>(cin);
sort(vec.begin(), vec.end(), compare);
copy(vec.begin(), vec.end(), ostream_iterator
<string>(cout));
cout
<<'\n';
}
return 0;
}

最后,从更抽象的角度看,排序的过程其实是在一个全序集合所构成的DAG中逐步遍历,最终到达零出度的最优点的过程。这同编程之美中的买书问题有异曲同工之处。

posted on 2011-03-31 18:08  acmaru  阅读(216)  评论(0编辑  收藏  举报

导航