Greedy 貪婪演算法

by __chang___

What is 貪婪演算法?

短視近利的演算法!

選擇當下看起來最好的做法

局部最佳的作法也是全部最佳的作法

如果題目有反例,就不能用貪婪演算法

實例&實作

我們需要具體的東ㄒ🤣

DESCRIPTION

給你一排藥水,藥水的數字代表喝下去後你的生命值的增加量

藥水的生命可能正可能負。

過程中完全不能讓生命值變成負的情況下,你最多可以喝幾瓶藥水?

SOLUTION

每個都先照順序加起來ㄅ

發現生命值變負了,再回去把生命值最小的捨棄掉不喝

反悔貪婪!

怎麼找到陣列中最小的數字?😧

用priority_queue把數字由小存到大(邊跑邊存)

EXPLANATION

i 0 1 2 3 4
arr[i] 8 -7 1 -4 -6
sum 8 1 2 -2 -8

ANS:

0

1

2

3

3

3

priority_queue存的內容:

8

8

-7

-7

1

8

-7

-4

1

8

-6

-4

1

8

+7

+7

+7

+7

+6

EXPLANATION

i 0 1 2 3 4
arr[i] 8 -7 1 -4 -6
sum 8 1 2 -2 -8

ANS:

0

1

2

3

3

3

priority_queue存的內容:

8

8

-7

-7

1

8

-7

-4

1

8

-6

-4

1

8

+7

+7

+7

+7

+6

CODE

#include<iostream>
#include<queue>
using namespace std;

int main(){	

	ios_base::sync_with_stdio(false);cin.tie(0);
	
	int n;cin>>n;
	long long int arr[n]={0};
	priority_queue<long long int,vector<long long int>,greater<long long int> > pq;
	
	long long int sum=0,ans=n;
	for(int i=0;i<n;i++){
		cin>>arr[i];
		pq.push(arr[i]);
		sum+=arr[i];
		while(sum<0){
			sum-=pq.top();
			pq.pop();
			ans--;
		}
	}
	
	cout<<ans<<'\n';
	

	return 0;
}

看起來像是初學者寫的扣 別噴😅

給你兩個陣列,請問各取一數所得的差最小是多少?

DESCRIPTION

SOLUTION

先把兩個陣列的數字由小排到大(sorting algorithm😋)

然後開始計算兩個數字之差

接著,有比較小的數字的陣列,將index往後一格

偵測到一個陣列的最後一個index為止

雙指針!

EXPLANATION

MIN DIF:

index 0 1 2 3 4
a[i] 1 5 7 8 11
b[j] 3 3 10 15 17
index 0 1 2 3 4
a[i] 1 5 7 8 11
b[j] 3 3 10 15 17

2

CURRENT DIF:

2

2

5

3

2

1

2

2

2

2

2

2

1

CODE

#include<iostream>
using namespace std;

int main(){	

	ios_base::sync_with_stdio(false);cin.tie(0);
	
	int n;cin>>n;
	long long int a[n]={0},sum=0;
	for(int i=0;i<n;i++)cin>>a[i];
	for(int i=0;i<n-1;i++){
		if(a[i]>a[i+1]){
			sum+=(a[i]-a[i+1]);
			a[i+1]=a[i];
		}
	} 
	cout<<sum<<'\n';

	return 0;
}

DESCRIPTION

把你切成L個部分,每個部份分別是a1,a2,a3,...,aN。

當你身體有x單位的身體被切割,身體會感受到x單位的痛苦

那麼,要達成題目所指定的要求所需最少的痛苦度是多少?

EXPLANATION

我才沒有直接抄題目的舉例ㄋ

假設你的身體總共10單位,要分成2,2,3,3?

+4

+6

+10

分割ㄉ狀態
{10}
{4,6}
{3,3,4}
{2,2,3,3}
分割ㄉ狀態
{10}
{2,8}
{2,2,6}
{2,2,3,3}

+6

+8

+10

+4

+6

+10

+6

+8

+10

P = 

4

+6

+10

P = 

6

+8

+10

SOLUTION

沒有錯,加回去的時候,每次都把最小的兩個元素加起來,最後痛苦度就會是最小的囉

假設要分成的單位是a1,a2,a3,...,aN,

其中 a1≤a2≤a3≤...≤aN

而每次切割所增加的痛苦度是pi,

痛苦度P=p1+p2+...+p(N-1)

那麼,只要將每次的pi都最小化

我們就可以得到最少的痛苦度了呢

CODE

#include<iostream>
#include<queue>
using namespace std;

int main(){	

	ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	
	priority_queue<long long int, vector<long long int>, greater<long long int> > pq;
	int n;
	while(cin>>n){
		int a;
		long long int sum=0;
		for(int i=1;i<=n;i++){
			cin>>a;
			pq.push(a);
		}
		for(int i=1;i<=n-2;i++){
			long long int b=pq.top();pq.pop();
			long long int c=pq.top();pq.pop();
			pq.push(b+c);sum+=b+c;
		}
		sum+=pq.top();pq.pop();sum+=pq.top();pq.pop();
		cout<<sum<<"\n";
	}
	
	return 0;
}
Made with Slides.com