洛谷-運輸(P2094)

題目鏈接:

運輸問題

問題分析:

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

#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;
}

關於優先級隊列更多細節可移步大佬博客:
優先級隊列(上)
優先級隊列(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章