一個裸的優先級隊列(最大堆)題,但也有其他普通隊列的做法。這道題我做了兩天,結果發現是輸入輸出太過頻繁,一直只能A掉55%的數據,其他都是TLE,如果將輸入輸出的數據放入緩存區,然后滿區輸出,可以將IO時間消耗降到很低。
任務調度(Schedule)
描述
某高性能計算集群(HPC cluster)采用的任務調度器與眾不同。為簡化起見,假定該集群不支持多任務同時執行,故同一時刻只有單個任務處於執行狀態。初始狀態下,每個任務都由稱作優先級數的一個整數指定優先級,該數值越小優先級越高;若優先級數相等,則任務名ASCII字典順序低者優先。此后,CPU等資源總是被優先級數最小的任務占用;每一任務計算完畢,再選取優先級數最小下一任務。不過,這里的任務在計算結束后通常並不立即退出,而是將優先級數加倍(加倍計算所需的時間可以忽略)並繼續參與調度;只有在優先級數不小於2^32時,才真正退出
你的任務是,根據初始優先級設置,按照上述調度原則,預測一批計算任務的執行序列。
輸入
第一行為以空格分隔的兩個整數n和m,n為初始時的任務總數,m為所預測的任務執行序列長度,每行末尾有一個換行符
以下n行分別包含一個整數和一個由不超過8個小寫字母和數字組成的字符串。前者為任務的初始優先級數,后者為任務名。數字和字符串之間以空格分隔
輸出
最多m行,各含一個字符串。按執行次序分別給出執行序列中前m個任務的名稱,若執行序列少於m,那么輸出調度器的任務處理完畢前的所有任務即可。
Example
Input
3 3
1 hello
2 world
10 test
Output
hello
hello
world
限制
0 ≤ n ≤ 4,000,000
0 ≤ m ≤ 2,000,000
0 < 每個任務的初始優先級 < 2^32
不會有重名的任務
時間:2 sec
內存:512 MB
解題思路:
首先是優先級隊列(最大堆),關於該問題的討論,可以參見我第一篇文章 算法手記 之 數據結構(堆)(POJ 2051)
建堆算法和插入算法在聽過鄧俊輝老師的MOOC后進行了優化,批量建堆操作可以將時間度綜合效率經過下濾優化至O(N),相對第一次接觸堆的時候有了較大的提高,手寫堆得代碼也可以因此變得更為簡潔。詳細算法參加下面的代碼。
其次是關於快速輸入輸出(FastIO),我在這里用結構體進行封裝,創建對象IO時可以完成構造函數的操作,包括stdin和stdout兩個流向,一個是最大400萬次輸入,一個是最大200萬次輸出,相信這樣可以將輸入輸出此時降至十位甚至個位數,將輸入輸出對時間的消耗降至極低的水平。
簡單介紹這里使用的一個設置文件緩存區的函數和另一個相似函數:
設置文件緩沖區函數
void setbuf(FILE *stream,char *buf);
void setvbuf(FILE *stream,char *buf,int type,unsigned size);
這兩個函數將使得打開文件后,用戶可建立自己的文件緩沖區,而不使用fopen()函數打開文件設定的默認緩沖區。
對於setbuf()函數,buf指出緩沖區長度,由stdio.h中定義的宏BUFSIZE的值決定,缺省為512字節。當buf為空時,setbuf函數將使的文件I/O不帶緩沖。
對setvbuf函數,則由malloc函數來分配緩沖區,參數size指明了緩沖區的長度。
type則表示了緩沖的類型,其值可以取如下值:
_IOFBF 文件全部緩沖,即緩沖區裝滿后,才能對文件讀寫
_IOLBF 文件行緩沖,即緩沖區接收到一個換行符時,才能對文件讀寫
_IONBF 文件不緩沖,此時忽略buf,size的值,直接讀寫文件,不再經過文件緩沖區緩沖
代碼的寫法模仿了一篇博客,在此表示感謝:terence-yang
具體代碼如下:
1 //優先級隊列+快速輸入輸出(批量) 2 //Time: 1508Ms Memory: 109488K(No.20) 3 #include<iostream> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 8 #define MAX 4000005 9 #define LCHILD(x) ((x)<<1) 10 #define RCHILD(x) (((x)<<1) + 1) 11 #define PRIOR(A,x,y) (A[x]>A[y]?(x):(y)) 12 13 int n, m; 14 const long long INF = (long long)1 << 32; //優先級數上限 15 const int SIZE = 1 << 21; //緩存區大小 16 17 /*快速輸入輸出緩存區設置*/ 18 struct FastIO { 19 char inbuf[SIZE]; 20 char outbuf[SIZE]; 21 FastIO() { 22 setvbuf(stdin,inbuf,_IOFBF,SIZE); 23 setvbuf(stdout,outbuf,_IOFBF,SIZE); 24 } 25 }IO; 26 27 struct Task { 28 char word[9]; 29 long long v; 30 bool operator > (Task &a){ /*重載為優先級比較符*/ 31 return v < a.v || v == a.v && strcmp(word, a.word) < 0; 32 } 33 }task[MAX]; 34 35 /*在parent和child之間找到最高優先級代替parent*/ 36 int replacePa(int x) 37 { 38 int pa = x; 39 if (RCHILD(x) <= n) 40 pa = PRIOR(task, x, PRIOR(task, LCHILD(x), RCHILD(x))); 41 else if (LCHILD(x) <= n) 42 pa = PRIOR(task, x, LCHILD(x)); 43 return pa; 44 } 45 46 /*下濾(向下調整堆)*/ 47 void percolateDown(int x) 48 { 49 int rp = replacePa(x); 50 while (rp != x) { 51 swap(task[rp], task[x]); 52 x = rp; 53 rp = replacePa(x); 54 } 55 } 56 57 /*批量建堆(堆積)*/ 58 void heapify() 59 { 60 for (int i = n / 2; i >= 1; i--) 61 percolateDown(i); 62 } 63 64 int main() 65 { 66 scanf("%d%d", &n, &m); 67 for (int i = 1; i <= n; i++) 68 scanf("%lld%s", &task[i].v, task[i].word); 69 heapify(); 70 71 for (int i = 0; n && i < m; i++) 72 { 73 printf("%s\n", task[1].word); 74 task[1].v *= 2; 75 if (task[1].v >= INF) 76 task[1] = task[n--]; 77 percolateDown(1); 78 } 79 80 return 0; 81 }