演算法系列學習 連續郵資問題

2022-08-18 00:06:11 字數 3069 閱讀 9424

王曉東老師編著的《計算機演算法設計與分析》5.12節以「連續郵資問題」為例展示了回溯法的應用。講解比較簡略,對於搜尋出一張新的郵票面值後如何更新最大連續郵資區間這一點沒有過多的說明。以下是自己對於這一節學習的一點筆記。

實際上,關於剛才所說的更新最大連續郵資區間的方法,可以歸結到一種「等價類」的思想。與此相似的還有《程式設計之美》中「陣列分割問題」的解法三,《程式設計之美》中「找符合條件的整數」的最後一種演算法,joj 1903,joj 1278這些題目等等。以下先從頭到尾把連續郵資問題複習一遍,然後小結一下這種「等價類」的方法。

連續郵資問題:某國家發行了n種不同面值的郵票,並且規定每張信封上最多隻允許貼m張郵票。連續郵資問題要求對於給定的n和m的值,給出郵票面值的最佳設計,在1張信封上貼出從郵資1開始,增量為1的最大連續郵資區間。例如,當n=5和m=4時,面值為的5種郵票可以貼出的最大連續郵資區間是1到70.

當然是用回溯法。搜尋結點的狀態應該是已經確定的郵票面值(各不相同並且總數不超過n)和它們能夠貼出的最大連續郵資區間,以此來列舉下乙個可能的郵票面值。因此,很自然地,使用原書中的識別符號,陣列x記錄當前已經確定的郵票面值,整數r表示當前使用不超過m張郵票能貼出的最大連續郵資區間。對於第i層的結點,x[1…i]表示當前已經有i個面值確定,r表示由x[1…i]能貼出的最大連續區間,現在,要想把第i層的結點往下擴充套件,有兩個問題需要解決:一,哪些數有可能成為下乙個的郵票面值,即x[i+1]的取值範圍是什麼;二,對於乙個確定的x[i+1],如何更新r的值讓它表示x[1…i+1]能表示的最大連續郵資區間。

第乙個問題很簡單,x[i+1]的取值要和前面i個數各不相同,最小應該是x[i] + 1,最大就是r+1,否則r+1沒有辦法表示。我們現在專注第二個問題。

第二個問題自己有兩種思路:一,計算出所有使用不超過m張x[1…i+1]中的面值能夠貼出的郵資,然後從r+1開始逐個檢查是否被計算出來。二,從r+1開始,逐個詢問它是不是可以用不超過m張x[1…i+1]中的面值貼出來。

兩種思路直接計算其計算量都是巨大的,需要借助動態規劃的方法。模仿0-1揹包問題,假設s(i)表示x[1…i]中不超過m張郵票的貼法的集合,這個集合中的元素數目是巨大的,例如,只使用1張郵票的貼法有c(i+1-1,1)=c(i,1)=i種,使用2張郵票的貼法有c(i+2-1,2)=c(i+1,2)=i*(i+1)/2種,……,使用m張郵票的貼法有c(i+m-1, m)種,其中c(n,r)表示n個元素中取r個元素的組合數。於是,s(i)中的元素的數目總共有c(i+1-1, 1) + c(i+2-1,2)+ … + c(i+m-1,m)個。s(i)中的每個元素就是一種合法的貼法,對應乙個郵資。當前最大連續郵資區間為1到r,那麼s(i)中每個元素的郵資是不是也在1到r之間呢?不一定,比如,當m=2時,它能貼出來8,但不能貼出來7,這一點自己在寫**時犯了錯誤。總之,在搜尋時,一定要保持狀態的一致性,即當深度搜尋到第i層時,一定要確保用來儲存結點狀態的變數中儲存的一定是第i層的這個結點的狀態。言歸正傳,定義s(i)中元素的值就是它所表示的貼法貼出來的郵資,於是,可以把s(i)中的元素按照它們的值的相等關係分成k類。第j類表示貼出郵資為j的所有的貼法集合,用t(j)表示,t(j)有可能是空集,例如對於,t(7)為空集,t(8)=}。此時有:s(i) = t(1) u t(2) u t(3) u … u t(k),u表示兩個集合的並。

現在考慮x[i+1]加入後對當前狀態s(i)的影響。假設s是s(i)中的乙個元素,即s表示一種合法的貼法,x[i+1]對s能貼出的郵資的影響就是x[i+1]的多次重複增加了s能貼出的郵資。這樣說是因為有兩種情況不需要考慮:一, 從s中去掉幾張郵票,把x[i+1]加進去,這沒有意義,因為從s中去掉幾張郵票後s就變成了s(i)中的另乙個元素t,我們遲早會對t考慮x[i+1]的影響的。二,將x[i+1]加入s,同時再把x[1]也加入s(如果s中還能再貼兩張郵票的話),這也沒有意義,原因同一。所以,x[i+1]對s的影響就是,如果s中貼的郵票不滿m張,那就一直貼x[i+1],直到s中有m張郵票,這個過程會產生出很多不同的郵資,它們都應該被加入到s(i+1)中。因為s屬於s(i),它也必定在某個t(k)中,而t(k)中能產生出最多不同郵資的是t(k)中用的郵票最少的那個元素。至此,原書中的解法就完全出來了:用陣列x記錄當前已經確定的郵票面值,用r表示當前最大的連續郵資區間,用陣列y表示用當前的面值貼出某個郵資所需要的最少的郵票數。狀態結點的轉換過程已經在上面說的非常清楚了。現在只差寫**了。**如下:

1 #include2 #include3 #include4 #include

5 #include6

7using

namespace

std;89

intn,m;

10int

maxstamp;

11int

r;12

const

int maxn=1e3;

13const

int inf=0x3f3f3f3f;14

intx[maxn];

15int

y[maxn];

16int

ans[maxn];

17void dfs(int

cur)

1828}29

return;30

}31intbackup_y[maxn];

32for(int i=0;i)

3336

int backup_r=r;

37for(int i=x[cur-1]+1;i<=r+1;i++)

3847

for(int num=1;num<=m-y[k];num++)

4853

}

54}

55while(y[r+1]

5659 dfs(cur+1

);60

for(int k=0;k)

6164 r=backup_r;65}

66}67void

print()

6874 printf("\n"

);75}76

intmain()

7788 dfs(1

);89

print();

9091

return0;

92 }

連續郵資問題

注意要寫while(y[r+1]不然最後的maxstamp是正確答案加一

演算法系列之 替換連續的字元

專案名稱 util 類名稱 replacestring 類描述 替換連續出現的字串 思路 將連續出現的字元位置設定為空,將為空的地方設定為要替換的字元 author 趙建銀 date 2018年1月10日 time 下午7 06 47 version 1.0 public class replace...

演算法系列 N皇后問題

常規n皇后解決問題過程 一 問題描述 運用回溯法解題通常包含以下三個步驟 1 針對所給問題,定義問題的解空間 2 確定易於搜尋的解空間結構 3 以深度優先的方式搜尋解空間,並且在搜尋過程中用剪枝函式避免無效搜尋 通過上述的基本思路,我們可以將問題描述為 x j 表示乙個解的空間,j表示行數,裡面的值...

演算法系列 約瑟夫斯問題

約瑟夫斯問題 有時也稱為約瑟夫斯置換 是乙個出現在電腦科學 和數學中的問題。在計算機程式設計 的演算法中,類似問題又稱為約瑟夫環。有個囚犯站成乙個圓圈 準備處決。首先從乙個人開始,越過 個人 因為第乙個人已經被越過 並殺掉第k個人。接著,再越過 個人,並殺掉第k個人。這個過程沿著圓圈一直進行,直到最...