書的複製(抄書問題)

2022-05-07 21:33:11 字數 2983 閱讀 3283

現在要把\(m\)本有順序的書分給\(k\)給人複製(抄寫),每乙個人的抄寫速度都一樣,一本書不允許給兩個(或以上)的人抄寫,分給每乙個人的書,必須是連續的,比如不能把第

一、第三、第四本書給同乙個人抄寫。

現在請你設計一種方案,使得複製時間最短。

輸出共\(k\)行,每行兩個整數,第\(i\)行表示第\(i\)個人抄寫的書的起始編號和終止編號。\(k\)行的起始編號應該從小到大排列,複製時間為抄寫頁數最多的人用去的時間,如果有多解,則盡可能讓前面的人少抄寫。

應同學的要求,我寫了這篇題解。這道題目的做法好像有兩種,動態規劃法和二分答案法,我們先講二分法。

二分答案的時候我們應該以每個人抄的書的本數作為二分標準,進行二分答案,如果分配的人數與題目要求的\(k\)不一樣,則將二分區間縮小,如果相等,我們還需要進一步縮小上界,使得用時最短。

現在具體來講一下二分答案的做法。首先確定上下界,因為每本書都會有人抄,那麼我們下界就可以確定為書的頁數的最大值(反證:如果二分的時候分到比這個最大值還小,那最大值的那本書就沒人抄了),以減少二分次數;接下來確定上界,很明顯,最壞情況就是只有乙個人抄書,那麼上界定為所有書頁數之和。

確定好上下界之後,我們就可以進行二分了。最外層肯定是套標準的二分模板,但是二分中間該怎麼處理呢?也不難,中間直接線性的掃一遍即可。需要注意的是,我們要使前面的人少抄,那麼就得從後面開始分配任務,給後面的人分配盡可能多的任務(但不超過當前限定值)。當二分到恰好\(k\)個人能抄完時,別急著退出迴圈,我們應該繼續縮小上界,求出最短時間。

具體實現如下:

#include using namespace std;

int main()

, man, co;

int st[25][505], en[25][505];

//醜得不堪入目qaq

cin >> m >> k;

for(i = 1; i <= m; i++)

if(k == 1)

while(l < h)

//二分模板

if(man > k)

l = mid + 1;

else

}for(i = k; i >= 1; i--)

cout << st[nmax][i] << ' ' << en[nmax][i] << endl;

return 0;

}

這是以前寫的醜**,請多多見諒。時間複雜度是\(o(m\lg\sum_^kcost[i])\)的,用時0ms。但是坑爹的vscode**出了強化版且不說,它竟然還有抄書員不抄書的情況!我的程式目前過不了那個情況\(^*\)

,有時間再改改。

動態規劃法似乎要慢一些,因為它相對於二分法來說,思維難度要簡單,並且細節方面也不太坑。

現在我們詳細講講動態規劃的做法。這道題可以套上類似於鋼條切割問題\(^1\)那樣的做法,也就是先不考慮所有的書,就考慮前面幾本書,將每種人數狀態\(i\)(\(1\leq i\leq k\))列舉出來,每種狀態都找出乙個最優解(可以通過前面的情況轉移獲得)。當書的數目這個狀態增加之後,我們列舉每乙個分割點,然後轉移即可。寫成遞推式就是這樣:

\[f[i][j]= \color\color, \color\color\}}

\]其中,\(f[i][j]\)表示\(i\)個人抄前\(j\)本書的最小耗費時間,\(sum[j]\)表示抄前\(j\)本書的時間,\(s\)代表當前列舉分割位置的狀態。看過鋼條切割的同學應該明白,新加的那個人直接抄完後面的所有書,而我們列舉了分割點\(s\),故不會遺漏,而裡面取\(\max\)是因為要獲得最大耗時,外面取\(\min\)是為了看看這個點之前算好了的狀態和正在算的狀態哪個更優,沒計算之前賦值為\(+\infty\)。

最後是列印的問題,學過鋼條切割問題的同學們都知道,我們列印只能遞迴列印。列印的方法也不難,就是遞迴的時候,以答案(抄書的最小時間)為標準,從後往前掃瞄以及累加,列印累加值恰好小於等於答案的那個掃瞄位置,並在下層遞迴之後列印。這個有點抽象,但是你看到**之後會恍然大悟的。

**實現\(^2\):

#include #include using namespace std;

int sum[505], f[505][505], m, k;

void print(int x, int ans)

}int main()

for(register int i = 2; i <= k; i += 1)

for(register int j = i; j <= m; j += 1)

for(register int s = 2; s <= j; s += 1)

f[i][j] = min(f[i][j], max(f[i-1][s-1], sum[j] - sum[s-1]));

print(m, f[k][m]);

return 0;

}

時間複雜度為:\(o(km^2)\),不過由於資料挺小的,開個\(o2\)或者把取最大最小值函式改成自己寫的差不多就能0ms了。

不過,動態規劃還有沒有可以優化的地步呢?答案是肯定的,但是時間上好像不能優化(博主本來想用四邊形不等式優化,但是經過討論之後發現有不止乙個情況不滿足條件)。

優化方案是:用滾動陣列優化空間。因為涉及到的轉移僅僅是\(f\)的左上位置,那麼我們只需要倒序更新即可。

滾動陣列優化部分:

for(register int i = 2; i <= k; i += 1)

for(register int j = m; j >= i; j -= 1)

for(register int s = 2; s <= j; s += 1)

f[j] = min(f[j], max(f[s-1], sum[j] - sum[s-1]));

thomas h.cormen, charles e.leiserson, ronald l.rivest, clifford stein. 演算法導論(第三版)[m]. 北京:機械工業出版社, 2012

hongzy. 題解 書的複製. 2017-12-21

Codevs 抄書問題1 2 3

對於抄書問題1,由於標籤上寫的dp,我就寫了dp。設dp i j 表示前i本書由j個人抄的最小答案,則狀態轉移方程為 dp i j min,其中s為字首和,k從j 1到1列舉。輸出方案時,由於題目要求多解時使前面的人抄的盡量少,因此可以貪心地輸出。由於我們在前面的dp過程後已經知道了每個人抄書的頁數...

書的複製 normal DP

time limit 1000ms memory limit 65536k total submit 164 accepted 83 description 現在要把m mm本有順序的書分給k kk個人複製 抄寫 每個人的抄寫速度都一樣,一本書不允許分給兩個或兩個以上的人抄寫,分給每個人的書,必須是...

書的複製 題解

現在要把m本有順序的書分給k個人複製 抄寫 每個人的抄寫速度都一樣,一本書不允許分給兩個或兩個以上的人抄寫,分給每個人的書,必須是連續的,比如不能把第 一 第三 第四本書給同乙個人抄寫。現在請你設計一種方案,使得複製時間最短。複製時間為抄寫最多的人用去的時間。第一行兩個整數,m,k k m 500 ...