貪心算法入門——區間問題


題目均來自acwing.com

AcWing 905. 區間選點

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

思路:每個區間遲早要選出一個符合要求的點,而對於一個大區間包含小區間的情況,小區間滿足大區間一定滿足,反之不亦然,所以不妨先考慮小區間。
按右端點從小到大排序。首先考慮第一個區間,反正一定要選一個點,不妨選右端點,顯然,這樣能盡可能覆蓋較多(左端點在第一段右端點的左邊的)區間,把這個點設為pos。依次枚舉每一個區間,如果它的左端點小於等於pos,則一定已經被覆蓋了;如果他的左端點大於pos,則需要選一個新點,同理可知應該選右端點,重復上述過程即可。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Seg {
  int l, r;
}seg[N];

bool cmp(Seg a, Seg b) {
  if (a.r != b.r) return a.r < b.r;
  else return a.l > b.l;
}

int main() {
  cin >> n;
  for (int i = 0; i < n; i++) cin >> seg[i].l >> seg[i].r;
  sort(seg, seg + n, cmp);
  int ans = 1, pos = seg[0].r;
  for (int i = 1; i < n; i++) 
    if (seg[i].l > pos) ans++, pos = seg[i].r;
  cout << ans;
  return 0;
}

AcWing 908. 最大不相交區間數量

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

思路:首先考慮第一個區間選哪個。按右端點從小到大排序,選擇右端點最小的區間,可以盡可能地提高選擇的余地。在數軸剩下的部分同樣這樣做即可。

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

struct Seg {
  int l, r;
} seg[N];

bool cmp(Seg a, Seg b) {
  return a.r < b.r;
}
int main() {
  cin >> n;
  for (int i = 0; i < n; i++) cin >> seg[i].l >> seg[i].r;
  sort(seg, seg + n, cmp);
  int ans = 1, pos = seg[0].r;
  for (int i = 1; i < n; i++) 
    if (seg[i].l > pos) ans++, pos = seg[i].r;
  cout << ans;
  return 0;
}

AcWing 906. 區間分組

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

思路:
圖書館中最少需要安排多少個座位,使得N個在不同時段[ai,bi]學習的人恰好都有座?
只要座位數大於同時學習的最大人數即可。。。(反正每個來到的人都會自動坐在空位上。。。)
我也不知道這叫啥思路,“人工智能”?
用差分+前綴和的話,數組有點大,自然想到離散化。
其實沒必要,將“坐下”和“離開”的所有操作按時間先后排序,按照時間軸依次進行(座位+1/-1)即可。
同一個時間點既有來學習的也有離開的,需要為來學習的人加一個座位。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, cnt, ans;

struct Node {
  int p, op;
} node[N];

bool cmp(Node a, Node b) {
  if (a.p != b.p) return a.p < b.p;
  else return a.op > b.op;
}

int main() {
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> node[i*2-1].p >> node[i*2].p;
    node[i*2-1].op = 1, node[i*2].op = -1;
  }
  sort(node + 1, node + 1 + n * 2, cmp);
  for (int i = 1; i <= n * 2; i++) {
    cnt += node[i].op;
    ans = max(ans, cnt);
  }
  cout << ans;
  return 0;
}

AcWing 907. 區間覆蓋

給定 N 個閉區間 [ai,bi] 以及一個線段區間 [s,t],請你選擇盡量少的區間,將指定線段區間完全覆蓋。輸出最少區間數,如果無法完全覆蓋則輸出 −1。

思路:
首先考慮覆蓋了s點的區間,顯然要選右端點最靠右的。
將所選區間的右端點當作新的s繼續操作即可。
掃一遍即可,具體實現見代碼。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int n, s, t;
struct Seg {
  int l, r;
} seg[N];

bool cmp(Seg a, Seg b) {
  return a.l < b.l;
}
int main() {
  cin >> s >> t >> n;
  for (int i = 0; i < n; i++) {
    cin >> seg[i].l >> seg[i].r;
  }
  sort(seg, seg + n, cmp);
  int pos = s, maxr = -inf, ans = 0;
  for (int i = 0; i < n; i++) {
    if (seg[i].l > pos) pos = maxr, ans++;
    if (seg[i].l <= pos && seg[i].r >= pos && seg[i].r > maxr) {
      maxr = seg[i].r;
    }
    if (pos >= t) break;
  }
  if (pos < t && maxr >= t) pos = maxr, ans++;
  if (pos >= t) cout << ans;
  else cout << -1;
  return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM