这里讨论的是贪心策略!
书中1.4首先给出了一个动态规划版本,我第一次看编程之美的时候,自己一下子也想到了动态规划版本,就没往后看了。书中讲到的动态规划版本此处不再重述。
今天重新看了下才知道后面的贪心才是精华。
(1)按照正常的思路,我们有了以下贪心策略:购买Y5本5卷(5卷是总数,不是指第5卷),购买Y4-Y5本4卷,购买Y3-Y4本3卷,购买Y3-Y4本3卷,购买Y2-Y3本2卷,购买Y1-Y2本1卷
(2)这样,按照上述策略,我们可以得到总代价为:
total_cost = Y5 * 5 * 8 * (1 - 25%) +
(Y4 - Y5) * 4 * 8* (1 - 20%) +
(Y3 - Y4) * 3 * 8* (1 - 10%) +
(Y2 - Y3) * 2 * 8* (1 - 5%) +
(Y1 - Y2) * 1 * 8* 100%
= 8 * (Y1 - Y2) +
15.2 * (Y2 - Y3)+
21.6 * (Y3 - Y4)+
25.6 * (Y4 - Y5)+
30 * Y5 -------------------- 式A
(3)观察式A,我们这个贪心策略一定是最优的吗?如果不是该怎么调整?(需要调整)
观察式A,我们可以把1个3卷和1个5卷合并成2个4卷,合并之前的所附付金额是 P1 = 21.6 + 30 = 51.6,
和并之后的金额是P2 = 2 × 25.6 = 51.2。 P2 < P1,所以这个合并调整可以取得更优的结果。其他的合并调整策略,如1个1卷和
1个3卷合成2个2卷,都会使所付金额偏大,因此不能使结果更优。所以调整的策略只有1个3卷和1个5卷合并调整成2个4卷。
(4)通过上述分析,我们得到最终贪心策略:
购买Y5 - K 本5卷,Y4 - Y5 + 2K本4卷,Y3 - Y4 - K本3卷, Y2 - Y3本2卷, Y1 - Y2本1卷, K = min(Y3 - Y4, Y5)。
如:我们要买的书用(7,6,3,2,1)表示。
Y1=7,Y2=6,Y3=3,Y4=2,Y5=1;
K=min(Y3-Y4,Y5)=1;
Y1-Y2=1;Y2-Y3=3;Y3-Y4-K=0; Y4 - Y5 + 2K=3; Y5– K=0。
即购买0本5卷,3本4卷,0本3卷,3本2卷,1本1卷。
由此可得解法如下:
代码:
const double BuyBook::UNIT_PRICE = 8;
const double BuyBook::DISCOUNTS[5] = {1,0.95, 0.9, 0.8, 0.75};
static const int BOOK_KINDS = 5;
double BuyBook::SearchCheapest(int* books)
{
Sort(books);//从大到小排列
int g[5];
g[0] = books[0] - books[1];
g[1] = books[1] - books[2];
g[2] = books[2] - books[3];
g[3] = books[3] - books[4];
g[4] = books[4];
int t = min(g[2], g[4]);
if (t > 0)//策略的调整
{
g[2] -= t;
g[4] -= t;
g[3] += 2 * t;
}
double sum = 0;
for (int i = 0; i < BOOK_KINDS;++i)
{
sum += g[i] *(i+1) * UNIT_PRICE * DISCOUNTS[i];
}
return sum;
}