原題:
https://cses.fi/problemset/task/1140
題意:
給 n 個專案,每個專案 [a,b] 完成可得利潤 p,任兩個被選專案不可重疊。求最大總利潤。
解題思路:
#include <bits/stdc++.h>
using namespace std;
struct Job { long long a,b,p; };
int main(){
int n;
cin>>n;
vector<Job> v(n);
for(int i=0;i<n;i++) cin>>v[i].a>>v[i].b>>v[i].p;
sort(v.begin(), v.end(), [](const Job& x, const Job& y){ return x.b < y.b; });
vector<long long> ends(n);
for(int i=0;i<n;i++) ends[i] = v[i].b;
auto find_prev = [&](int i){ // 最大 j < i 使得 ends[j] < v[i].a
int j = upper_bound(ends.begin(), ends.end(), v[i].a - 1) - ends.begin() - 1;
return j; // -1 表示沒有
};
vector<long long> dp(n+1, 0); // dp[0]=0,對應使用 0 個工作
for(int i=1;i<=n;i++){
int j = find_prev(i-1) + 1; // 轉成 dp 的索引(+1)
dp[i] = max(dp[i-1], dp[j] + v[i-1].p);
}
cout << dp[n] << "\n";
return 0;
}
原題:
https://cses.fi/problemset/task/1140
題意:
n 部電影,每部 [s,e],最多有 k 個人同時看(每人同時只能看一部且不可重疊)。求能看的最大總場次。
解題思路:
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, k;
cin>>n>>k;
vector<pair<long long,long long>> mv(n);
for(int i=0;i<n;i++) cin>>mv[i].first>>mv[i].second; // s, e
sort(mv.begin(), mv.end(), [](auto &x, auto &y){ return x.second < y.second; });
multiset<long long> ms; // 每個人的最後結束時間
long long ans = 0;
for(auto &[s,e]: mv){
auto it = ms.upper_bound(s);
if(it == ms.begin()){
if((int)ms.size() < k){
ms.insert(e);
ans++;
} // 否則:無法安排
}else{
--it; // 使用能接續的那位
ms.erase(it);
ms.insert(e);
ans++;
}
}
cout << ans << "\n";
return 0;
}
原題:
https://leetcode.com/problems/maximum-profit-in-job-scheduling/description/
題意:
與 CSES Projects 相同。
解題思路:
將 startTime/endTime/profit 打包成工作,依結束時間排序,二分 prev,做 DP。
class Solution {
public:
int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
int n = startTime.size();
struct Job{int s,e,p;};
vector<Job> v(n);
for(int i=0;i<n;i++) v[i] = {startTime[i], endTime[i], profit[i]};
sort(v.begin(), v.end(), [](const Job& a, const Job& b){ return a.e < b.e; });
vector<int> ends(n);
for(int i=0;i<n;i++) ends[i] = v[i].e;
auto prev_idx = [&](int i){
int j = upper_bound(ends.begin(), ends.end(), v[i].s - 1) - ends.begin() - 1;
return j;
};
vector<long long> dp(n+1, 0);
for(int i=1;i<=n;i++){
int j = prev_idx(i-1) + 1;
dp[i] = max(dp[i-1], dp[j] + v[i-1].p);
}
return dp[n];
}
};
原題:
https://leetcode.com/problems/non-overlapping-intervals/
題意:
刪除最少區間使得其餘區間互不重疊。
解題思路:
選最多不重疊區間(結束時間最早優先),答案 = n - 選到的數量。
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if(intervals.empty()) return 0;
sort(intervals.begin(), intervals.end(), [](auto& a, auto& b){
if(a[1] != b[1]) return a[1] < b[1];
return a[0] < b[0];
});
int cnt = 0, lastEnd = INT_MIN;
for(auto &it: intervals){
if(it[0] >= lastEnd){
cnt++;
lastEnd = it[1];
}
}
return (int)intervals.size() - cnt;
}
};