題目鏈接:
問題分析:
初讀題目,可能有點搞不懂。題目要求:二當一(兩件的價格算一件),並且給出了這個價格是如何計算的。題目的關鍵點在:如此反覆。直到只收一件商品的錢。 分析到這,是否感覺有點類似哈夫曼編碼以及洛谷中的另一道貪心習題-合併果子?
按照這個思路,寫出如下的代碼:
#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;
}