這個問題是《算法導論》上的一個經典的貪心算法問題——單處理器上具有期限和懲罰的單位時間任務調度問題,目標是使懲罰最小。
輸入:第一行一個數n,接下來第2行到第n+1行,第i行分別是d[i]和w[i],其中d[i]表示第i個任務的時限,w[i]表示第i個任務的懲罰,每個任務的完成時間為單位時間。
輸出:一行一個數,為最小懲罰。
題解:
令集合S為所有任務的集合,A為一個任務集合,其中所有的任務滿足條件:存在某種調配方式,使得所有任務能在期限前完成,又令l為所有A集合的集合。可以證明,序對M=(S,l)是一個擬陣,於是,可以采用貪心算法解決它。同時,一個任務集合R屬於l,當且僅當它滿足下列條件:
R中在期限在不大於t的任務不大於t個。
於是算法就出來了:先以w[i]為關鍵字進行排序,再進行枚舉:保持一個數組record[i]保存當前集合中時限在不大於i的任務個數,對於每一個任務ai,將d[i]之后的record更新(加1),若有一個d[j]不滿足條件,則繼續檢查下一個,反之,將它的懲罰從總懲罰中減去。最后得到的總懲罰即最小懲罰。
代碼:
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 ifstream fin("TSP.in"); 5 ofstream fout("TSP.out"); 6 int w[2001],d[2001]; //w[i]指第i號任務的逾期懲罰,d[i]指第i號任務的期限,每個任務都是單位時間; 7 int record[2001]; 8 int Max,ans; 9 int partion(int *a,int start,int end) 10 { 11 int i=start,j=start-1,t=0; 12 for(i=start;i<=end;i++) 13 { 14 if(a[i]>=a[end]) 15 { 16 j++; 17 t=a[i]; 18 a[i]=a[j]; 19 a[j]=t; 20 t=d[i]; 21 d[i]=d[j]; 22 d[j]=t; 23 } 24 } 25 return j; 26 } 27 int quicksort(int *a,int start,int end) 28 { 29 if(start>=end) 30 return 0; 31 int j=partion(a,start,end); 32 quicksort(a,start,j-1); 33 quicksort(a,j+1,end); 34 return 0; 35 } 36 int main() 37 { 38 int n=0,i=0,j=0; 39 fin>>n; 40 for(i=1;i<=n;i++) 41 { 42 fin>>d[i]>>w[i]; 43 } 44 quicksort(w,1,n); 45 for(i=1;i<=n;i++) 46 { 47 if(d[i]>=n+1) 48 { 49 Max+=w[i]; 50 continue; 51 } 52 for(j=d[i];j<=n;j++) 53 { 54 if(record[j]+1>j) 55 break; 56 } 57 if(j==n+1) 58 { 59 for(j=d[i];j<=n;j++) 60 record[j]++; 61 Max+=w[i]; 62 } 63 } 64 for(i=1;i<=n;i++) 65 ans+=w[i]; 66 ans-=Max; 67 fout<<ans<<endl; 68 }
擬陣作為一種高級結構,是在貪心算法的研究中所必須掌握的,同時也由於它的難度較大,更需要我們來認真學習。
更多關於擬陣的內容,請參見《算法導論(第二版)》的“貪心算法”一章。