這次是一點關於貪心的看法,輕噴QAQ
什麼是貪心?
要說貪心算法,罵我們首先就要知道什麼是貪心。
通俗一點來講,我覺得貪心就是我就要最好的;
而站在程序設計的角度的話,貪心就是做出當前情況下最好的選擇,也就是局部最優解;這同時也意味着貪心並不一定會得到最優解,但一定是當前的最優解。
那麼什麼時候適合使用貪心呢?
- 具有最優子結構。把問題劃分以後存在最優解(這個最有子結構和動態規劃的最優子結構應該是類似的,不過動態規劃我還沒搞懂,就不瞎逼逼了);
- 最優解的可證明性。也就是說能證明所求問題的最優解存在在子問題裏,畢竟我們想要的還是那個最優解。
難受的是,證明最優解的存在不是那麼容易,而驅使我們使用貪心的動力就是它的思想容易理解,一但證明最優解存在,那麼構造起來比較容易了。感覺好糾結
經典例題
貪心算法比較經典的問題就是揹包問題了,這裏又可以分成0-1揹包問題和部分揹包問題。
0-1揹包問題就是一個物品要麼完全放入揹包,要麼完全不放入揹包,部分揹包問題就是物品不用完全放入揹包,其中我們可以知道0-1揹包問題無法用貪心求出最優解(證明),而部分揹包問題是可以求出的,我們只需要按需部分選取單位重量價值最大的物品就能得到最優解。比如:
揹包容量50
物品:A B C
重量:25 20 10
價值:30 25 10
它們的單位價值從小到大依次爲B-1.25、A-1.2、C-1,揹包容量又爲40,那麼我們只要選取20的B,25的A和5的C就能得到最高價值60。
實例
最後我們來看幾個實際問題。
混合牛奶
由於乳製品產業利潤很低,所以降低原材料(牛奶)價格就變得十分重要。幫助Marry乳業找到最優的牛奶採購方案。
Marry乳業從一些奶農手中採購牛奶,並且每一位奶農爲乳製品加工企業提供的價格是不同的。此外,就像每頭奶牛每天只能擠出固定數量的奶,每位奶農每天能提供的牛奶數量是一定的。每天Marry乳業可以從奶農手中採購到小於或者等於奶農最大產量的整數數量的牛奶。
給出Marry乳業每天對牛奶的需求量,還有每位奶農提供的牛奶單價和產量。計算採購足夠數量的牛奶所需的最小花費。
直接上代碼,不難看懂
#include<iostream>
#include<algorithm> //調用sort(),pair<>
#define maxSize 20000 //不能太大,否則會溢出
using namespace std;
/*
輸入格式:
第 1 行共二個數值:N,(0<=N<=2,000,000)是需要牛奶的總數;M,(0<= M<=5,000)是提供牛奶的農民個數。
第 2 到 M+1 行:每行二個整數:Pi 和 Ai。
Pi(0<= Pi<=1,000) 是農民 i 的牛奶的單價。
Ai(0 <= Ai <= 2,000,000)是農民 i 一天能賣給Marry的牛奶製造公司的牛奶數量。
輸出格式:
單獨的一行包含單獨的一個整數,表示Marry的牛奶製造公司拿到所需的牛奶所要的最小費用。
*/
int main(){
int n,m,i;
pair<int,int> milk[maxSize]; //pair<>動態數組,裏面有first和second兩個參數可調用
cin>>n>>m;
for(i = 0;i < m;i++){ //first是需要牛奶總量,second是提供牛奶戶人數
cin>>milk[i].first>>milk[i].second;
}
sort(milk,milk+m);
int price = 0,sum = 0;
//貪心算法,部分
for(i = 0;i < m;i++){
if(sum + milk[i].second <= n){ //牛奶戶提供牛奶量不超過需求時
price += milk[i].first*milk[i].second;
sum += milk[i].second;
}
else{
price += milk[i].first*(n-sum);
break;
}
}
cout<<price<<endl; //花費最少
system("pause");
return 0;
}
排隊接水
有n個人在一個水龍頭前排隊接水,假如每個人接水的時間爲Ti,請編程找出這n個人排隊的一種順序,使得n個人的平均等待時間最小。
上
#include<iostream>
#include<algorithm>
#include<iomanip> //cout輸出浮點數
#define maxSize 1000
using namespace std;
int main(){
int n,i;
double sum = 0.0;
cin>>n;
pair<int,int> queue[maxSize];
for(i = 0;i < n;i++){
cin>>queue[i].first; //first爲等待時間
queue[i].second = i+1; //second爲序號
}
sort(queue,queue+n); //爲使平均等待時間最小,從小到大排序
for(i = 0;i < n;i++){ //計算到第n個人接水時等待時間
sum += queue[i].first*(n-i-1);
cout<<queue[i].second<<" ";
}
cout<<endl;
cout<<fixed<<setprecision(2)<<sum/n; //格式化輸出浮點數
system("pause");
return 0;
}