首頁 > 軟體

C++實現貪婪演演算法的範例詳解

2022-07-06 10:01:54

區間問題

區間選點

給定 N 個閉區間 [ai,bi],請你在數軸上選擇儘量少的點,使得每個區間內至少包含一個選出的點。

輸出選擇的點的最小數量。

位於區間端點上的點也算作區間內。

輸入格式

第一行包含整數 N,表示區間數。

接下來 N 行,每行包含兩個整數 ai,bi,表示一個區間的兩個端點。

輸出格式

輸出一個整數,表示所需的點的最小數量。

資料範圍

1≤N≤1e5,

−1e9≤ai≤bi≤1e9

先對右端點進行排序,有交集的區間進行右端點的更新,沒有交集則點數+1。

#include<bits/stdc++.h>
using namespace std;
 
const int N=1e5+10;
struct node{
    int a,b;
    bool operator<(const node&w)const {
        return b<w.b;}
}range[N];

int main(){
    int n;
    cin>>n;
    int a,b;
    for(int i=0;i<n;i++){
        cin>>a>>b;
        range[i]={a,b};
    }
    sort(range, range+n);
    int s=-2e9,cnt=0;
    for(int i=0;i<n;i++){
        if(s<range[i].a){
            cnt++;
            s=range[i].b;
        }
    }
    cout<<cnt;
    return 0;
}

最大不相交區間數量

給定 N 個閉區間 [ai,bi],請你在數軸上選擇若干區間,使得選中的區間之間互不相交(包括端點)。

輸出可選取區間的最大數量。

輸入格式

第一行包含整數 N,表示區間數。

接下來 N 行,每行包含兩個整數 ai,bi,表示一個區間的兩個端點。

輸出格式

輸出一個整數,表示可選取區間的最大數量。

資料範圍

1≤N≤1e5,

−1e9≤ai≤bi≤1e9

先對右端點進行排序,有交集的區間進行右端點的更新,沒有交集則點數+1。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;

struct node{
    int a,b;
    bool operator<(const node&w)const{
        return b<w.b;
    }
}range[N];
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        int a,b;
        cin>>a>>b;
        range[i]={a,b};
    }
    int res=0,s=-2e9;
    sort(range,range+n);
    for(int i=0;i<n;i++){
        if(range[i].a>s){
            s=range[i].b;
            res++;
        }
    }
    cout<<res;
    return 0;
    
}

區間分組

給定 N 個閉區間 [ai,bi],請你將這些區間分成若干組,使得每組內部的區間兩兩之間(包括端點)沒有交集,並使得組數儘可能小。

輸出最小組數。

輸入格式

第一行包含整數 N,表示區間數。

接下來 N 行,每行包含兩個整數 ai,bi,表示一個區間的兩個端點。

輸出格式

輸出一個整數,表示最小組數。

資料範圍

1≤N≤1e5,

−1e9≤ai≤bi≤1e9

先區分左右端點進行排序,再遍歷取左右 端點未抵消的最大值。

#include<bits/stdc++.h>

using namespace std;

const int N = 100010;

int n;
int b[2 * N], idx;

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		int l, r;
		cin >> l >> r;
		b[idx++] = l * 2;
		b[idx++] = r * 2 + 1;//用奇偶性區分左右端點
	}
	sort(b, b + idx);
	int res = 1, t = 0;
	for (int i = 0; i < idx; i++) {
		if (b[i] % 2 == 0)t++;
		else t--;
		res = max(res, t);
	}
	cout << res;
	return 0;
}

優先佇列做法。

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
struct Range {
    int l, r;
    bool operator <(const  Range& w)const {
        return l < w.l;
    }
}range[N];
int n;
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        int l, r;
        cin >> l >> r;
        range[i] = { l,r };}
    sort(range, range + n);
    int res = 0,ed=-2e9;
    
    priority_queue<int, vector<int>, greater<int>>heap;
    for (int i = 0; i < n; i++) {
        auto r = range[i];
        if (heap.empty() || heap.top() >= r.l)heap.push(r.r);
        else {
            int t = heap.top();
            heap.pop();
            heap.push(r.r);
        }
    }
    cout << heap.size();
    return 0;
}

區間覆蓋

給定 N 個閉區間 [ai,bi] 以及一個線段區間 [s,t],請你選擇儘量少的區間,將指定線段區間完全覆蓋。

輸出最少區間數,如果無法完全覆蓋則輸出 −1。

輸入格式

第一行包含兩個整數 s 和 t,表示給定線段區間的兩個端點。

第二行包含整數 N,表示給定區間數。

接下來 N 行,每行包含兩個整數 ai,bi,表示一個區間的兩個端點。

輸出格式

輸出一個整數,表示所需最少區間數。

如果無解,則輸出 −1。

資料範圍

1≤N≤1e5,

−1e9≤ai≤bi≤1e9,

−1e9≤s≤t≤1e9

#include<bits/stdc++.h>

using namespace std;

const int N = 100010;
struct  Range {
	int l, r;
	bool operator <(const Range& w)const {
		return l < w.l;
	}
}range[N];

int main() {
	int n;
	int st, ed;
	cin >> st >> ed;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int l, r;
		cin >> l >> r;
		range[i] = { l,r };

	}
	sort(range, range + n);
	int res = 0;
	bool sc = false;
	for (int i = 0; i < n; i++) {
		int j = i, r = -2e9;
		while (j < n && range[j].l <= st) {
			r = max(r, range[j].r);
			j++;
		}
		if (r < st) {
			res = -1;
			break;
		}
		res++;
		if (r >= ed) {
			sc = true;
			break;
		}
		i = j-1;
		st = r;
	}
	if (!sc)cout << -1;
	else cout << res;
	return 0;
}

Huffman樹

合併果子

在一個果園裡,達達已經將所有的果子打了下來,而且按果子的不同種類分成了不同的堆。

達達決定把所有的果子合成一堆。

每一次合併,達達可以把兩堆果子合併到一起,消耗的體力等於兩堆果子的重量之和。

可以看出,所有的果子經過 n−1 次合併之後,就只剩下一堆了。

達達在合併果子時總共消耗的體力等於每次合併所耗體力之和。

因為還要花大力氣把這些果子搬回家,所以達達在合併果子時要儘可能地節省體力。

假定每個果子重量都為 1,並且已知果子的種類數和每種果子的數目,你的任務是設計出合併的次序方案,使達達耗費的體力最少,並輸出這個最小的體力耗費值。

例如有 3 種果子,數目依次為 1,2,9。

可以先將 1、2 堆合併,新堆數目為 3,耗費體力為 3。

接著,將新堆與原先的第三堆合併,又得到新的堆,數目為 12,耗費體力為 12。

所以達達總共耗費體力=3+12=15。

可以證明 15 為最小的體力耗費值。

輸入格式

輸入包括兩行,第一行是一個整數 n,表示果子的種類數。

第二行包含 n 個整數,用空格分隔,第 i 個整數 ai 是第 i 種果子的數目。

輸出格式

輸出包括一行,這一行只包含一個整數,也就是最小的體力耗費值。

輸入資料保證這個值小於 231。

資料範圍

1≤n≤10000,

1≤ai≤20000

只需要用優先佇列先取出兩個,再插入一個,直至最後剩下一個。

#include<iostream>
#include<algorithm>
#include<queue>
#include<bits/stdc++.h>
using namespace std;

int main() {
	int n;
	cin>>n;
	priority_queue<int, vector<int>, greater<int>>heap;

	while (n--) {
		int x;
		cin >> x;
		heap.push(x);
	}
	int res = 0;
	while (heap.size() > 1) {
		int a = heap.top();
		heap.pop();
		int b = heap.top();
		heap.pop();
		int c = a + b;
		heap.push(c);
		res += c;
	}
	cout << res;
	return 0;
}

排序不等式

排隊打水

有 n 個人排隊到 1 個水龍頭處打水,第 i 個人裝滿水桶所需的時間是 ti,請問如何安排他們的打水順序才能使所有人的等待時間之和最小?

輸入格式

第一行包含整數 n。

第二行包含 n 個整數,其中第 i 個整數表示第 i 個人裝滿水桶所花費的時間 ti。

輸出格式

輸出一個整數,表示最小的等待時間之和。

資料範圍

1≤n≤1e5,

1≤ti≤1e4

值正序,下標倒序相乘得到最小值

#include<bits/stdc++.h>
using namespace std;

const int N = 100010;
int a[N];
int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a, a + n);
	int x=n;
	long long res=0;
	for (int i = 0; i < n; i++) {
		res += a[i] * (x - 1);
		x--;
	}
	cout << res;
	return 0;
}

絕對值不等式

貨艙選址

在一條數軸上有 N 家商店,它們的座標分別為 A1∼AN。

現在需要在數軸上建立一家貨倉,每天清晨,從貨倉到每家商店都要運送一車商品。

為了提高效率,求把貨倉建在何處,可以使得貨倉到每家商店的距離之和最小。

輸入格式

第一行輸入整數 N。

第二行 N 個整數 A1∼AN。

輸出格式

輸出一個整數,表示距離之和的最小值。

資料範圍

1≤N≤100000,

0≤Ai≤40000

只需統計各點到中位數的距離之和。

#include <bits/stdc++.h>
using namespace std;
const int N=100100;
int a[N],n,i,ans,sum;
int main()
{
    cin>>n;
    for (i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+1+n);//排序
    int sm=a[n/2+1];//中位數
    for (i=1;i<=n;i++)
        ans=ans+abs(a[i]-sm);//統計和中位數之間的差
    cout<<ans;
    return 0;
}

以上就是C++實現貪婪演演算法的範例詳解的詳細內容,更多關於C++ 貪婪演演算法的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com