Greedy 貪婪
Yeedrag owo
甚麼是貪婪?
甚麼是貪婪?
例子: 新台幣有1,5,10,100元,如果我想要湊出總價為\(k\)的值,最少要幾個硬幣?
當然是優先換大的幣值R
甚麼是貪婪?
A:在每一步採用當前看起來最好的選擇,進而希望使最終答案最好的方法
使用前要想想,這個題目貪婪確定會是對的嗎?
現實中貪婪演算法的例子
貪婪的吃雞塊直到癱瘓,得到人類吃雞塊上界
現實中貪婪演算法的例子
貪婪的吃便當,直到吃飽為止
你們學過的貪婪例子
BubbleSort泡沫排序:
每次遍歷攤貪婪的把大的換上去
SelectionSort選擇排序:
每次遍歷貪婪的找出最大值,並且放到最後
Priority Queue:
每次插入時,大的往前面走
怎麼想出一個
好的貪婪方式?
數學歸納法
通靈(?
實際練習owo
(上禮拜的PQ困難題)
貪婪做法:
每次把目前最小的兩個加起來
(不嚴謹的)說明:
假設有四個數字\(a,b,c,d\),且\(a<b<c<d\)
和\(c<d<(a+b)\),則:
照順序合併下去:
\(sum1 = (a+b)+((a+b)+c)+((a+b)+c+d)\)
每次選最小的合併:
\(sum2 = (a+b)+(c+d)+((a+b)+c+d)\)
\(sum1<sum2\) 發現每次取最小會比任何沒取最小好
另外想法:觀察後發現後面加值進總和時,加進去的值會和前面合併的有關,故希望前面合併越小越好
用pq實作的扣的:
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int main(){
ios_base::sync_with_stdio(false); cin.tie(0);
int n;
while(cin>>n&&n!=0){
priority_queue<int,vector<int>,greater<int>> pq;
for(int i=0;i<n;i++){
int tmp;
cin>>tmp;
pq.push(tmp);
}
long long sum = 0;
while(pq.size()!=1){
int a = pq.top();
pq.pop();
int b = pq.top();
pq.pop();
pq.push(a+b);
sum+=(a+b);
}
cout<<sum<<endl;
}
}
上禮拜TOI潛力組的第一題owo
怎麼做?
分情況!
1.目前的種類和前個最新種類不一樣:
把最新的種類改成目前種類
把總和加上去
2.目前的種類和前個最新種類一樣:
比較前一個值和新的哪個比較大,大的換上去
扣的:
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int main(){
ios_base::sync_with_stdio(false); cin.tie(0);
int n,k;
cin>>n>>k;
int cur = 0;//紀錄目前拿取最新的種類
int curnum = 0;//紀錄目前拿取最新的價值
int yum[n];//美味度
int type[n];//種類
for(int i=0;i<n;i++){
cin>>yum[i];//輸入價值
}
for(int i=0;i<n;i++){
cin>>type[i];//輸入種類
}
long long int sum = 0;
for(int i=0;i<n;i++){
if(cur==type[i]){
if(yum[i]>curnum){
//比較同種類,誰的價值大
sum+=(yum[i]-curnum);
curnum = yum[i];
}
} else {
//第一種情況,直接貪婪拿取
sum += yum[i];//價值加進總和
cur = type[i];//更新種類
curnum = yum[i];//更新價值
}
}
cout<<sum<<endl;
}
自己試試看想一題吧~
有四種情況:
吃最慢的先
吃最快的先
做最慢的先
做最快的先
哪一個比較好?
#include<iostream>
#include<algorithm>
#include<queue>
#include<climits>
#include<vector>
using namespace std;
bool cmp(pair<int,int> a,pair<int,int> b){
return a.second>b.second;//吃得慢的放前面
}
int main(){
ios_base::sync_with_stdio(false); cin.tie(0);
int n;
while(cin>>n&&n){
vector<pair<int,int>> vec;
for(int i=0;i<n;i++){
int a,b;
cin>>a>>b;
vec.push_back(make_pair(a,b));
}
long long int ans = LLONG_MIN;//include<climits>
long long int sum = 0;
sort(vec.begin(),vec.end(),cmp);
for(int i=0;i<n;i++){
sum+=vec[i].first;
ans = max(ans,(long long)sum+vec[i].second);
}
cout<<ans<<endl;
}
}
Code:
反悔貪婪
先做再說,後果之後再看ㄅ
給你一個數列,可以從左至右取數字,問在
過程中數字和不小於零的情況下,最多可以
拿取幾個數字?
做法:
先遇到數字就放進總和,
如果總和小於零了,就把有參與目前總和的
值中代價最大(扣最多)的持續減掉直到總和>=0
4 | -4 | 1 | -3 | 1 | -3 |
---|
總和:
4
0
1
-2 (+4) = 2
3
0
A:5個
扣的
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int main(){
priority_queue<long long,vector<long long>,greater<long long>> pq;
//pq維護代價最大的藥水
int n;
cin>>n;
long long hp = 0;
long long cnt = 0;
while(n--){
int k;
cin>>k;
hp+=k;
cnt++;
pq.push(k);
while(hp<0){//生命少於0 開始反悔
cnt--;
hp-=pq.top();
pq.pop();
}
}
cout<<cnt<<endl;
}
雙指針
給你兩個數列,求兩個數列中
各取一數最小的差是多少?
直觀想法:
每個數字都掃一遍數列找最小
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
int main(){
ios_base::sync_with_stdio(false); cin.tie(0);
int n,m;
cin>>n>>m;
int array1[n];
int array2[m];
for(int i=0;i<n;i++){
cin>>array1[i];
}
for(int i=0;i<m;i++){
cin>>array2[i];
}
int ans = 2147483647;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
ans = min(ans,abs(array1[i]-array2[j]));
}
}
cout<<ans<<endl;
}
複雜度:
\(O(NM)\)
TLE....
雙指針!
2 | 3 | 5 |
---|
-1 | 7 | 8 |
---|
Array1
Array2
最小差:
3
2
每次把目前指針比較小的往上一格,
直到其中一個指針到最後。
扣的:
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<vector>
using namespace std;
int main(){
ios_base::sync_with_stdio(false); cin.tie(0);
int n,m;
cin>>n>>m;
int array1[n];
int array2[m];
for(int i=0;i<n;i++){
cin>>array1[i];
}
for(int i=0;i<m;i++){
cin>>array2[i];
}
sort(array1,array1+n);
sort(array2,array2+n);//要先sort過才能雙指針
int p1 = 0; int p2 = 0;//兩個指針
int ans = 2147483647;
while(p1<n&&p2<m){
ans = min(ans,abs(array1[p1]-array2[p2]));
if(array1[p1]<array2[p2]){
p1++;//指到小的指針往上一格
} else {
p2++;
}
}
cout<<ans<<endl;
}
時間複雜度:
\(O(N+M)\)
好耶AC
無情ㄉ題單
都比上課例題簡單很多 應該沒問題ㄅ owo
想要變態題單可以來要ㄛㄛ
Greedy+2ptr
By yeedrag
Greedy+2ptr
- 737