题目链接:
问题分析:
初读题目,可能有点搞不懂。题目要求:二当一(两件的价格算一件),并且给出了这个价格是如何计算的。题目的关键点在:如此反复。直到只收一件商品的钱。 分析到这,是否感觉有点类似哈夫曼编码以及洛谷中的另一道贪心习题-合并果子?
按照这个思路,写出如下的代码:
#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a, int b){
return b < a;
}
int main(){
int n, k;
cin >> n >> k;
int ars[n];
for(int i = 0; i < n; i++)
cin >> ars[i];
sort(ars, ars+n, cmp);
for(int i = 0, j; i < n-1; i++){//如题解,每次选取最大两个值进行运算(合并果子?)
ars[i+1] = (ars[i] + ars[i+1]) / k;
int t = ars[i+1];
for(j = i+2; j < n && t < ars[j]; j++){//为新值找合适位置
ars[j-1] = ars[j];//向前移动
}
ars[j-1] = t;
}
cout << ars[n-1];
return 0;
}
提交后发现,思路正确。但是不禁有两个疑问:a.为什么采取上述策略可以有效解决问题呢?b.上述代码的时间复杂度显然是O(n^2),耗时的操作主要浪费在为新值寻找合适位置上了。是否存在有效的解决方案降低时间复杂度呢?看了很多博文,大佬们用的都是优先队列,不妨通过此类题目学习一下优先级队列。按照上述两个疑问,本篇博文一下将分别分析。
疑问A分析:
题目要求运费最少,则最优解为运费最少的情况。分析运费的计算情况,如何让运费最少?我们不妨将上述新运费计算方式当作一种打折活动,买东西时,我们自然希望贵重的物品的折扣越大越好,或者打的折扣次数尽量多。回到本题,若每次最贵重的物品参与的/k次数越多,最终的费用也不久最少吗?相比选择便宜的物品,打相同次数的折扣自然要比贵重物品少很多。因此问题不难得证。
优先级队列:
其使用方法同普通队列没有什么区别,先看下其一般使用方法:
#include<bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int>que;
queue<int> q;
for(int i = 0; i < 10; i++){
int t = rand() % 10;
q.push(t);
que.push(t);
}
cout << "priority_queue:" << endl;
for(int i = 0; i < 10; i++){
cout << que.top() << " ";
que.pop();
}
cout << "\nqueue:" << endl;
for(int i = 0; i < 10; i++){
cout << q.front() << " ";
q.pop();
}
return 0;
}
通队列基本没有什么区别,不同的是,其是按序排列的。通过上述实践,也可以发现:优先级队列默认按最大值优先的顺序。
倘若我们想改变优先级顺序,该如何实现?c++提供了自带的库函数,当然我们也可以自定义。先看下自带的库函数操作:
#include<bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int, vector<int>, greater<int> > que_greater;
priority_queue<int, vector<int>, less<int> > que_less;//默认效果
priority_queue<int, vector<int>, equal_to<int> > que_equal_to;
priority_queue<int, vector<int>, greater_equal<int> > que_greater_equal;
priority_queue<int, vector<int>, less_equal<int> > que_less_equal;
for(int i = 0; i < 10; i++){
int t = rand() % 10;
que_greater.push(t);
que_less.push(t);
que_equal_to.push(t);
que_greater_equal.push(t);
que_less_equal.push(t);
}
cout << "greater:" << endl;
for(int i = 0; i < 10; i++){
cout << que_greater.top() << " ";
que_greater.pop();
}
cout << "\nque_greater_equal:" << endl;
for(int i = 0; i < 10; i++){
cout << que_greater_equal.top() << " ";
que_greater_equal.pop();
}
return 0;
}
自定义比较操作:
#include<bits/stdc++.h>
using namespace std;
struct cmp1{
bool operator()(int &a, int &b){
return a < b;
}
};
struct cmp2{
bool operator()(int &a, int &b){
return a > b;
}
};
int main(){
priority_queue<int, vector<int>, cmp1> que_cmp1;
priority_queue<int, vector<int>, cmp2> que_cmp2;
for(int i = 0; i < 10; i++){
int t = rand() % 10;
que_cmp1.push(t);
que_cmp2.push(t);
}
cout << "cmp1:\n";
for(int i = 0; i < 10; i++){
cout << que_cmp1.top() << " ";
que_cmp1.pop();
}
cout << "\ncmp2:\n";
for(int i = 0; i < 10; i++){
cout << que_cmp2.top() << " ";
que_cmp2.pop();
}
return 0;
}
将上述题目用优先级队列重写一下:
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
int main(){
int n, k;
cin >> n >> k;
priority_queue<int>que;
for(int i = 0; i < n; i++){
int t;
cin >> t;
que.push(t);
}
for(int i = 0, j; i < n-1; i++){
int x, y, z;
x = que.top();
que.pop();
y = que.top();
que.pop();
z = (x+y) / k;
que.push(z);
}
cout << que.top();
return 0;
}