學校測試-2015-2-27,測試-2015-2-27
題目一 描述
關鍵子工程(project.c/cpp/pas)
在大型工程的施工前,我們把整個工程劃分為若干個子工程,並把這些子工程編號為1、2、……、N;這樣劃分之後,子工程之間就會有一些依賴關係,即一些子工程必須在某些子工程完成之後才能施工。由於子工程之間有相互依賴關係,因此有兩個任務需要我們去完成:首先,我們需要計算整個工程最少的完成時間;同時,由於一些不可預測的客觀因素會使某些子工程延期,因此我們必須知道哪些子工程的延期會影響整個工程的延期,我們把有這種特徵的子工程稱為關鍵子工程,因此第二個任務就是找出所有的關鍵子工程,以便集中精力管理好這些子工程,盡量避免這些子工程延期,達到用最快的速度完成整個工程。為了便於編程,現在我們假設:
(1)根據預算,每一個子工程都有一個完成時間。
(2)子工程之間的依賴關係是:部分子工程必須在一些子工程完成之後才開工。
(3)只要滿足子工程間的依賴關係,在任何時刻可以有任何多個子工程同時在施工,也既同時施工的子工程個數不受限制。
(4)整個工程的完成是指:所有子工程的完成。
輸入資料
第1行為N,N是子工程的總個數,N≤200。
第2行為N個正整數,分別代表子工程1、2、……、N的完成時間。
第3行到N+2行,每行有N-1個0或1。其中的第I+2行的這些0,1,分別表示“子工程I”與子工程1、2、…、I-1、I+1、…N的依賴關係,(I=1、2、……、N)。每行資料之間均用一個空格分開。
輸出資料:
如子工程劃分不合理,則輸出-1;
如子工程劃分合理,則用兩行輸出:第1行為整個工程最少的完成時間。第2行為按由小到大順序輸出所有關鍵子工程的編號。
分析
-
如果子工程 A 必須在 B 之後完成, 那麼從 B->A 連邊。然後拓撲排序,如果出現環,說明任務無法完成。邊拓撲排序邊邊計算這個結點之前完成的最小時間。記錄它之前最後一個完成的工程和時間。
-
找到最後完成的任務,根據之前記錄的它之前最後一個完成的工程一層層找到最後一個任務,也就是一條最長路。
- 但是,最長路可能並不只一條,所以可以用 vector 記錄所有的最長路。我第一次嘗試這樣做。詳見代碼。
- 後來看題解發現可以在最後去找關鍵路徑而不是在一開始拓撲排序就記錄關鍵路徑。這樣可以省很多事。
代碼
https://code.csdn.net/snippets/608610
題目二 描述
機器分配(machine.c/cpp/pas)
某總公司擁有高效生產裝置M台,準備分給下屬的N個分公司。各分公司若獲得這些裝置,可以為總公司提供一定的盈利。問:如何分配這M台裝置才能使國家得到的盈利最大?求出最大盈利值。
分配原則:每個公司有權獲得任意數目的裝置,但總台數不得超過總裝置數M。其中M<=100,N<=100。
輸入資料:
第一行為兩個整數M,N。接下來是一個N×M的矩陣,其中矩陣的第i行的第j列的數Aij表明第i個公司分配j台機器的盈利。所有資料之間用一個空格分隔。
輸出資料:
只有一個資料,為總公司分配這M台裝置所獲得的最大盈利。
分析
- DP
f[i][j]表示前 i 個公司分到 j 台機器的最大盈利
f[i][j] = max{f[i][j], f[i-1][j-k] + A[i][k](0≤k≤j)}
f[n][m]
代碼
https://code.csdn.net/snippets/608633
題目三 描述
編輯距離(edit.c/cpp/pas)
字串是資料結構和電腦語言裡很重要的資料類型,在電腦語言中,對於字串我們有很多的操作定義,因此我們可以對字串進行很多複雜的運算和操作。實際上,所有複雜的字串操作都是由字串的基本操作組成。例如,把子串a替換為子串b,就是用尋找、刪除和插入這三個基本操作實現的。因此,在複雜字串操作的編程中,為了提高程式中字元操作的速度,我們就應該用最少的基本操作完成複雜操作。
在這裡,假設字串的基本操作僅為:刪除一個字元、插入一個字元和將一個字元修改成另一個字元這三種操作。
我們把進行了一次上述三種操作的任意一種操作稱為進行了一步字元基本操作。
下面我們定義兩個字串的編輯距離:對於兩個字串a和b,通過上述的基本操作,我們可以把a變成b或b變成a;那麼,把字串a變成字串b需要的最少基底字元操作步數稱為字串a和字串b的編輯距離。
例如,如a=“ABC”,b=“CBCD”,則a與b的編輯距離為2。
你的任務就是:編一個最快的程式來計算任意兩個字串的編輯距離。
輸入資料:
第1行為字串a;第2行為字串b。註:字串的長度不大於1000,字串中的字元全為大寫字母。
輸出資料:
編輯距離。
分析
代碼
https://code.csdn.net/snippets/608645
題目四 描述
在現實生活中,我們經常遇到硬幣找零的問題,例如,在發工資時,財務人員就需要計算最少的找零硬幣數,以便他們能從銀行拿回最少的硬幣數,並保證能用這些硬幣發工資。我們應該注意到,人民幣的硬幣系統是100,50,20,10,5,2,1,0.5,0.2,0.1,0.05,0.02,0.01元,採用這些硬幣我們可以對任何一個工資數用貪心演算法求出其最少硬幣數。
但不幸的是:我們可能沒有這樣一種好的硬幣系統,因此用貪心演算法不能求出最少的硬幣數,甚至有些金錢總數還不能用這些硬幣找零。例如,如果硬幣系統是40,30,25元,那麼37元就不能用這些硬幣找零;95元的最少找零硬幣數是3。又如,硬幣系統是10,7,5,1元,那麼12元用貪進法得到的硬幣數為3,而最少硬幣數是2。
你的任務就是:對於任意的硬幣系統和一個金錢數,請你編程求出最少的找零硬幣數;如果不能用這些硬幣找零,請給出一種找零方法,使剩下的錢最少。
輸入資料:
第1行,為N和T,其中1≤N≤500為硬幣系統中不同硬幣數;1≤T≤10000為需要用硬幣找零的總數。
第2行為N個數值不大於65535的正整數,它們是硬幣系統中各硬幣的面值。
輸出資料:
如T能被硬幣系統中的硬幣找零,請輸出最少的找零硬幣數。
如T不能被硬幣系統中的硬幣找零,請輸出剩下錢數最少的找零方案中的所需的最少硬幣數。
分析
- 完全背包
f[i]表示找錢數為 i 時最少的硬幣數量。
f[i] = min{f[i], f[i-A[j]] + 1(A[j]≤i)} // 注意判斷
f[t]
- 如果
f[t] == INF,從 t 向前找到第一個不為INF的值f[t1],t - t1就是找零後剩下的最小錢數。
- 什麼是找零後剩下的最小錢數?找回的錢比設定要少(覺得十分不合理…),讓這個差值儘可能小
代碼
https://code.csdn.net/snippets/608653
- 改了好幾遍才都改對,以上代碼都可以 AC 了。
- 各種細節 :
- STL的
unique並不真正把重複的元素刪除,是把重複的元素移到後面去了。返回一個迭代器,它自己和其後的元素都是原來重複的元素
- 數組越界的判定
- DP時的條件判定
INF = 0x3f3f3f3f, memset(0x3f) 的寫法可以放心用。致謝 Archon
- 。。。