题目如下:
很久很久以前,有一位国王拥有5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人人数也不同。
如果参与挖掘的工人总数是10,每座金矿要么全挖,要么不挖,要得到尽可能多的金矿。
①200kg / 3人
②300kg / 4人
③350kg / 3人
④400kg / 5人
⑤500kg / 5人
贪婪算法:
对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
针对性价比:
①350kg / 3人 ,人均产量 116.6kg
②500kg / 5人 ,人均产量 100kg
③400kg / 5人 , 人均产量 80kg
④300kg / 4人 , 人均产量 75kg
⑤200kg / 3人 , 人均产量 66.6kg
结果选择前两名 850kg / 8人。
结论:得到局部最优解,但不是最好答案。
动态规划:
把复杂的问题简化成规模较小的子问题,再从简单的子问题自底向上一步一步递推。
举个例子,设金矿数量为n, 工人数量为w, 所需工人数量是p[] (数组), 产量是g[] (数组)
对于本文例子:
n = 5, w = 10, p[] = {5, 5, 3, 4, 3}, g[] = {400, 500, 200, 300, 350}
简化问题成:2个子问题
①前4个金矿,用10个工人
②前4个金矿,用7个工人 和 第5个金矿, 用3个工人
以此递推下去,显而易见时间复杂度是O(),是无法接受的。。。
并且在递推过程中,分化问题会有相同(重复)情况出现。
优化:
根据动态规划的自底向上的特性。
1个工人 | 2个工人 | 3个工人 | 4个工人 | 5个工人 | 6个工人 | 7个工人 | 8个工人 | 9个工人 | 10个工人 | |
400kg黄金/5人 | 0 | 0 | 0 | 0 | 400 | 400 | 400 | 400 | 400 | 400 |
500kg黄金/5人 | 0 | 0 | 0 | 0 | 500 | 500 | 500 | 500 | 500 | 900 |
200kg黄金/3人 | 0 | 0 | 200 | 200 | 500 | 500 | 500 | 700 | 700 | 900 |
300kg黄金/4人 | 0 | 0 | 200 | 300 | 500 | 500 | 500 | 700 | 800 | 900 |
350kg黄金/3人 | 0 | 0 | 350 | 350 | 500 | 500 | 500 | 850 | 850 | 900 |
时间复杂度变成O(n*w) 即O(5 * 10)
时间复杂度达到最优,降低空间复杂度。
可以发现每一行数据都是由上一行推导出来的。
例如:4个金矿、9个工人的最优结果是由3个金矿、9个工人 和 3个金矿、5个工人结果推导。
贴出代码:
int getBestGold(int n, int w, int p[], int g[])
{
int *results = new int[w+1];
memset(results, 0, 4 * (w + 1));
for(int i = 1; i <= n; i++)
{
for(int j = w; j >= i; j--)
{
if(j >= p[i-1])
{
results[j] = max_<int>(results[j],
results[j - p[i-1]] + g[i - 1]);
}
}
}
cout << "result >> ";
for(int i = 1; i <= w; i++)
{
cout << results[i] << " ";
}
cout << endl;
return results[w];
}
void main()
{
int w = 10;
int n = 5;
int p[] = {5, 5, 3, 4, 3};
int g[] = {400, 500, 200, 300, 350};
cout << "gold max >> " << getBestGold(n, w, p, g);
system("pause");
}
输出结果: