题目:POJ - 3253
书中例题。
在看题解之前自己做了一下,用了错误的贪心策略:“越短的木棒切割的时间应该越晚”,局部来看是对的。
正确策略的局部也是这样的。
不过自己的想法不够全面,只想到了第一步。
例如需要1,2,3,4,5 五根木棒,我觉得应该:
先花15的代价切下来长度为5的,
再花10的代价切下来长度为4的,
再花6的代价切下来长度为3的,
最后花3的代价切下来长度为2的,
剩下的也就是长度为1的。
这样代价一共是15+10+6+3=34,而最优策略代价为33.
因此, 虽然说越长的木棒应该最先被切没错,但是对于选择最长木棒这件事,并不是直接从题目所需要的所有长度的木棒里面选最长的,而是:两根次长的也许可以拼成一根比当前最长更长的。
因此应该从小往大遍历,不断选取最短的两根木棒组成新木棒加入所需队列中,换个角度就是说:最短的木棒与次短的木棒应该是兄弟节点。
仔细领会错误和正确策略的区别(懒得开Visio了✧(≖ ◡ ≖✿)):
1+2拼成3,3+3拼成6,此时需要6、5、4三根木棒,6便是最大的,而不是从一开始受限的角度所得出的5。
代码:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 int n; 7 int plank[20000]; 8 9 void slove() { 10 long long res = 0; 11 12 while (n > 1) { 13 int min = 0; //最小的板的下标 14 int minNext = 1; //次小的板的下标 15 16 if (plank[min] > plank[minNext]) { 17 swap(min, minNext); 18 } 19 20 for (int i = 2; i < n; i++) { //找到最小和次小的两块板 21 if (plank[i] < plank[min]) { 22 minNext = min; 23 min = i; 24 } 25 else if (plank[i] < plank[minNext]) { 26 minNext = i; 27 } 28 } 29 30 int t = plank[min] + plank[minNext]; 31 res += t; 32 33 if (min == n - 1) { //将拼合后的板加入到数组中,去掉原来的 34 swap(min, minNext); 35 } 36 plank[min] = t; 37 plank[minNext] = plank[n - 1]; 38 39 n--; 40 } 41 cout << res << endl; 42 } 43 44 int main() { 45 cin >> n; 46 for (int i = 0; i < n; i++) { 47 cin >> plank[i]; 48 } 49 slove(); 50 51 return 0; 52 }