题目:有n个商店,分布在x[i]位置上,每个商店每天需要进货w[i]吨,问送货站建在哪里,代价最小?
分析:
进货量:w[0] w[1] w[2] ... w[i] w[i+1] w[i+2] ... w[n]
位 置:x[0] x[1] x[2] ... x[i] x[i+1] x[i+2] ... x[n]
送货站: x^
假设送货站的位置在x处,如果x位于x[i]和x[i+1]之间,那么送货的代价是:
w[0]*(x - x[0]) + w[1]*(x - x[1]) + ... + w[i]*(x - x[i]) + w[i+1]*(x[i+1] - x) + w[i+2]*(x[i+2] -x) + ... w[n]*(x[n] - x) 化简为下面形式:
(w[i+1]*x[i+1] + w[i+2]*x[i+2] + ... + w[n]*x[n] - w[0]*x[0] - w[1]*x[1] - ... - w[i]*x[i]) + (w[0] + w[1] + ... + w[i] - w[i+1] - w[i+2] - ... - w[n])*x,x取值范围是x[i] 到 x[i+1]
如果(w[0] + w[1] + ... + w[i] - w[i+1] - w[i+2] - ... - w[n])为正值,x应该取x[i],反之,x应该取x[i+1],这样可以使得代价最小
从1到n-1遍历所有的i,分别计算两部分的值,将计算的代价值存储在一个数组中,扫描一遍数组,取出其中的最小代价值,就可以确定出x的位置。
在计算循环的过程中要反复计算 sum(w[0]x[0] ... w[i]x[i]) 和sum(w[i+1]x[i+1] ... w[n]x[n])这需要一层循环,再加上i本身的一层循环,算法的复杂度是O(n^2)的,其实sum的计算可以通过sum(w[0]x[0] ... w[i]x[i]) = sum(w[0]x[0] ... w[i-1]x[i-1]) + w[i]x[i]来计算,而sum(w[i+1]x[i+1] ... w[n]x[n]) = sum(w[0]x[0] ... w[n]x[n]) - sum(w[0]x[0] ... w[i]x[i])来计算,事先可以通过一次扫描计算出sum(w[i+1]x[i+1] ... w[n]x[n]),后面就不用再循环计算sum了,从而减少了一层循环,降低了复杂度到O(n)
预先计算和最后的求最小代价都是O(n),因此整个算法的复杂度为O(n)
程序如下:
#include <stdio.h>
#include <boost/scoped_ptr.hpp>
int DeliverySelection(int weight[], int position[], int length) {
boost::scoped_ptr<int> cost_buffer(new int[length]);
int* cost = cost_buffer.get();
int sum_weight = 0;
for (int i = 0; i < length; ++i) {
sum_weight += weight[i];
}
int sum_cost = 0;
for (int i = 0; i < length; ++i) {
sum_cost += weight[i] * position[i];
}
int cost_ending_here = 0;
int weight_ending_here = 0;
int pos = 0;
for (int i = 0; i < length; ++i) {
cost_ending_here += weight[i] * position[i];
weight_ending_here += weight[i];
if (weight_ending_here - (sum_weight - weight_ending_here) > 0) {
pos = position[i];
} else {
pos = position[i + 1];
}
cost[i] = (sum_cost - cost_ending_here) - cost_ending_here +
(weight_ending_here - (sum_weight - weight_ending_here)) * pos;
}
int min_cost = 0x7FFFFFFF;
int min_pos = -1;
for (int i = 0; i < length; ++i) {
if (cost[i] < min_cost) {
min_cost = cost[i];
min_pos = i;
}
}
return position[min_pos];
}
int main(int argc, char** argv) {
int weight[] = {2, 8, 5, 2, 3, 5, 6, 8, 1};
int position[] = {0, 3, 7, 10, 11, 13, 15, 17,19};
int array_size = sizeof(weight) / sizeof(int);
int selected_position = DeliverySelection(weight, position, array_size);
printf("DeliveryPosition:%d\n", selected_position);
}
相似问题:
编程之美 1.8