zz: http://www.strongczq.com/2012/03/srm536-div2-3-mergersdivtwo.html
题目原文:http://community.topcoder.com/stat?c=problem_statement&pm=11802&rd=14728
题目原文:http://community.topcoder.com/stat?c=problem_statement&pm=11802&rd=14728
题目大意:
将n个公司合并为一个公司,可以分多次合并,每次合并的公司数量至少为k个,一次合并产生的新公司收益为各参与合并公司的收益均值。现给定数组int[] rs 表示这n个公司的收益,问最终合并后的公司收益最大值可以是多少。
数据规模:n为[2,50], k为[2,n], 每个公司的收益范围为[-1000,1000]
思路:
题中描述的公司合并方式存在一个规律:就是一个公司参与合并的次数越多,那么他对最终公司收益值的影响越小。因为每次合并m个公司,相当于各个参与合并公司的收益都除以m,一个公司参与合并的次数越多最终除的次数也越多,对最终公司收益值的影响也越小。根据这个规律我们可以猜想最优合并策略中,每次合并都是选取收益最小的哪些公司进行合并的,而如此合并出的新公司收益必定又是最小的,所以合并的过程实际上类似与一颗只沿着一边往上增长的树。
根据这个猜想,
考虑使用dp算法。先让rs数组从小到大排序,dp方程为f(m),表示前m个公司进行多次合并得到的最大收益值。状态转移方程为,计算f(m)时考虑以下情况:
- 如果m< 2*k - 1,则只能对这m个公司进行一次合并,计算这些公司的收益均值即可;
- 否则,考虑最优合并策略下最后一次合并有j个公司,那么这j个公司的收益必然是f(m - j + 1), rs[m - j + 1], ..., rs[m - 1],计算这些公司的均值即可。
算法的时间复杂度为O(n*n),空间复杂度为O(n)。
下面证明以上的猜想。

公司的合并过程可以用如上图所示的一个树来表示,叶子节点表示最原始的n个公司。将rs数组从小到大排序,用w[]表示最优合并策略中这n个公司在最终收益中占的权重,则w数组必然也是从小到大排序的。这里定义上图中完全由叶子节点合并的过程为“原始合并”,属于同一个原始合并的公司必然拥有相同的权重。则权重最小的那个原始合并包含的公司必然是收益最小的那一批,那么这些公司可以作为第一次合并的公司。也就是说,必然存在一个最优合并策略,每一次合并都是选取收益最小的那一批公司。
Java代码:
public class MergersDivTwo {
public double findMaximum(int[] rs, int k) {
Arrays.sort(rs);
int n = rs.length;
double[] dp = new double[n];
Arrays.fill(dp, -2000);
for (int len = k; len <= n; ++len) {
double sum = 0;
for (int j = len - 1; j >= 0; --j) {
if (j >= k - 1 && len - j >= k) {
double ave = (sum + dp[j]) / (len - j);
dp[len - 1] = Math.max(dp[len - 1], ave);
}
sum += rs[j];
}
dp[len - 1] = Math.max(dp[len - 1], sum / len);
}
return dp[n - 1];
}