贪心算法 -- 最小延迟调度
轉自:https://blog.csdn.net/bqw18744018044/article/details/80285414
總結:
? 首先,證明貪心的時候交換論證是萬能的!其次,這一點如果要滿足,也就是,如果你要用交換論證法,那么首先要保證交換逆序后,對其他的沒有影響!如果有影響,那就只能像【POJ - 3253】Fence Repair?這道題一樣,用優先隊列去解決了。
1. 單區間調度問題
問題定義:存在單一資源,有一組以時間區間形式表示的資源請求reqs={req-1, req-2, …, req-n},第i個請求希望占用資源一段時間來完成某些任務,這段時間開始于begin(i)終止于end(i)。如果兩個請求req-i和req-j在時間區間上沒有重疊,則說這兩個請求是相容的,求出這組請求的最大相容子集(最優子集)。舉個例子:有一間多媒體課室,某一個周末有多個社團想要申請這間課室去舉辦社團活動,每個社團都有一個對應的申請時間段,比如周六上午8:00-10:00。求出這間課室在這個周末最多能滿足幾個社團的需求。
解決方案:貪心算法,優先選擇最早結束的需求,確保資源盡可能早地被釋放,把留下來滿足其他需求的時間最大化。具體偽代碼如下所示,算法結束后集合A中會保留所有相容請求,A的大小即是最大相容數量。
初始化R是所有需求的集合,A為空集
對R中的需求Ri,根據結束時間從早到晚排序
for Ri in R, do
? if Ri與A中的請求相容
? ? A = A并Ri
? endIf
endFor
return A
上述偽代碼的C++實現如下,
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAX_SIZE = 100;
struct Request {
? int begin, end;
} req[MAX_SIZE];
bool operator<(const Request& req1, const Request& req2) {
? return req1.end < req2.end;
}
int main() {
? int requestNum;
? cin >> requestNum;
? if (requestNum > MAX_SIZE) {
? ? cout << "請求數量過多" << endl;
? ? return 0;
? }
? for (int i = 0; i < requestNum; ++i) {
? ? cin >> req[i].begin >> req[i].end;
? }
? sort(req, req + requestNum);
? vector<Request> rvec;
? rvec.push_back(req[0]);
? for (int i = 1; i < requestNum; ++i) {
? ? if (rvec[rvec.size() - 1].end <= req[i].begin) {
? ? ? rvec.push_back(req[i]);
? ? }
? }
? cout << "最大兼容量: " << rvec.size() << endl;
? return 0;
}
2. 多區間調度問題
問題定義:存在多個(或者無限多個)相同的資源,有一組以時間區間形式表示的資源請求reqs={req-1, req-2, …, req-n},第i個請求希望占用資源一段時間來完成某些任務,這段時間開始于begin(i)終止于end(i)。如果兩個請求req-i和req-j在時間區間上沒有重疊,則說這兩個請求是相容的,用盡可能少的資源滿足所有請求(求最優資源數量)。舉個例子:有很多間課室,某個周末有多個社團需要申請課室辦活動,每個社團都有一個對應的申請時間,求最少需要多少間課室才能夠滿足所有社團的需求(在這個問題之中時間重疊的社團需要安排在其他課室,即會使用到多個資源,需要考慮多個資源上的調度安排,故稱為多區間調度)。
解決方案:貪心算法,將需求按照開始時間的早晚進行排序,然后開始為這些資源打標簽,每個標簽代表都一個資源,需求req-i被打上標簽k表示該請求分配到的資源是k。遍歷排序后的需求,如果一個需求與某個已分配資源上的其他安排不沖突,則把該需求也放進該資源的安排考慮中;如果沖突,那么應該要給此需求分配新的資源,已用資源數量加一。具體操作的偽代碼如下所示。
對n個需求按照開始時間從早到晚進行排序
假設排序后的需求記為{R1, R2, ..., Rn}
初始化tagSize = 1;
for i=1 to n, do:
? tags = {1,2,...,tagSize};
? for j = 1 to i-1, do:
? ? if Rj與Ri時間區間重疊產生沖突:
? ? ? tags = tags - {Rj的標簽};
? ? endIf
? endFor
? if tags為空集:
? ? tagSize += 1;
? ? 將標簽tagSize貼在Ri上
? EndIf
? else:
? ? 在tags剩下的標簽中隨便挑一個貼給Ri
? endElse
endFor
此時每個請求上都貼有標簽,每個標簽對應其申請的資源編號,此時的tagSize就是至少需要的資源數量
return tagSize;
上述偽代碼的C++實現如下:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX_SIZE = 100;
struct Request {
? int begin, end, tag;
} req[MAX_SIZE];
bool operator<(const Request& req1, const Request& req2) {
? return req1.begin < req2.begin;
}
int main() {
? int requestNum;
? cin >> requestNum;
? if (requestNum > MAX_SIZE) {
? ? cout << "請求數量過多" << endl;
? ? return 0;
? }
? for (int i = 0; i < requestNum; ++i) {
? ? cin >> req[i].begin >> req[i].end;
? }
? sort(req, req + requestNum);
? int tagSize = 1;
? req[0].tag = 0;
? bool tags[MAX_SIZE];
? for (int i = 1; i < requestNum; ++i) {
? ? memset(tags, 1, sizeof(tags));
? ? for (int j = 0; j < i; ++j) {
? ? ? if (req[j].end > req[i].begin) {
? ? ? ? tags[req[j].tag] = false;
? ? ? }
? ? }
? ? bool isTagsEmpty = true;
? ? int tag;
? ? for (int j = 0; j < tagSize; ++j) {
? ? ? if (tags[j]) {
? ? ? ? isTagsEmpty = false;
? ? ? ? tag = j;
? ? ? ? break;
? ? ? }
? ? }
? ? if (isTagsEmpty) {
? ? ? req[i].tag = tagSize;
? ? ? ++tagSize;
? ? } else {
? ? ? req[i].tag = tag;
? ? }
? }
? cout << "最小資源使用量: " << tagSize << endl;
? return 0;
}
3. 最小延遲調度問題
問題定義:存在單一資源和一組資源請求reqs={req-1, req-2, …, req-n},與前面兩個問題不同,這里的資源從時刻0開始有效(開始接受申請,開始可以被使用),每個請求req-i都有一個截止時間ddl(i),每個請求都要占用資源一段連續的時間來完成任務,占用時間為time(i)。每個請求都希望自己能在ddl之前完成任務,不同需求必須被分在不重疊的時間區間(單一資源,同一時刻只能滿足一個請求)。假設我們計劃滿足每個請求,但是允許某些請求延遲(即某個請求在ddl之后完成,延誤工期),確定一種合理的安排,使得所有請求的延期時間中的最大值,是所有可能的時間安排情況中最小的。從時刻0開始,為每個請求req-i分配一個長度time(i)的時間區間,把區間標記為[begin(i), end(i)],其中end(i) = begin(i) + time(i)。如果end(i) > ddl(i),則請求req-i被延遲,延遲時間為delay(i) = end(i) - ddl(i);否則delay(i) = 0。合理安排需求,使得maxDelay = max{delay(1), delay(2), …, delay(n)}是所有可能的安排中最小的。
解決方案:貪心算法,按照截止時間ddl排序,越早截止的任務越早完成。該算法是一個沒有空閑的最優調度,即從時刻0開始都有在處理請求,直到最后一個請求執行完釋放資源之后才空閑。偽代碼如下所示。
將需求按照截止時間進行排序
假設排序后的截止時間為ddl[1]<=...<=ddl[n]
start = 0;
maxDelay = 0;
for i = 1 to n, do:
? begin[i] = start;
? end[i] = start + time[i];
? start = end[i] + time[i];
? if maxDelay < end[i] - ddl[i]:
? ? L = end[i] - ddl[i];
? endIf
endFor
則每個任務安排的時間區間為[begin[i], end[i]],所有任務中最大的延遲為maxDelay,maxDelay為所有可能的任務安排中最小的延遲
return maxDelay;
上述代碼的C++實現如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX_SIZE = 100;
struct Request {
? int time, ddl;
? int begin, end;
} req[MAX_SIZE];
bool operator<(const Request& req1, const Request& req2) {
? return req1.ddl < req2.ddl;
}
int main() {
? int requestNum;
? cin >> requestNum;
? if (requestNum > MAX_SIZE) {
? ? cout << "請求數量過多" << endl;
? ? return 0;
? }
? for (int i = 0; i < requestNum; ++i) {
? ? cin >> req[i].time >> req[i].ddl;
? }
? sort(req, req + requestNum);
? int start = 0, maxDelay = 0;
? for (int i = 0; i < requestNum; ++i) {
? ? req[i].begin = start;
? ? req[i].end = start + req[i].time;
? ? start += req[i].time;
? ? if (maxDelay < req[i].end - req[i].ddl) {
? ? ? maxDelay = req[i].end - req[i].ddl;
? ? }
? }
? cout << "最小的最大延遲: " << maxDelay << endl;
? return 0;
}
轉自:https://blog.csdn.net/hongchh/article/details/52183614
代碼格式不做調整,詳情請去原博主博客中看。
總結
以上是生活随笔為你收集整理的贪心算法 -- 最小延迟调度的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 神舟十三号航天员返回74天后首次亮相:感
- 下一篇: 雷军:是徕卡找的我们