連續郵資問題

2021-08-18 06:44:37 字數 3912 閱讀 5000

/*   

連續郵資問題   

演算法設計:   

該問題是設計最佳的郵票面值,用來表示最大的區間   

對於連續郵姿問題,用n元組x[1:n]表示n種不同的郵票面值並約定它們從小到大排列。   

整數r表示當前使用不超過m張郵票能貼出的最大連續郵資區間。 

x[1] = 1是唯一的選擇。此時最大連續郵資區間是[1:m]   

接下來x[2]的可能取值範圍是[2:m+1],這個取值範圍就決定了x[2]應該怎麼選,   

只所以確定這樣的範圍是因為,至少要保證增加乙個面值後,可以將區間增   

大1一般情況下,已選定x[1:i-1],此時最大連續郵資區間是[1:r],則接下來   

x[i]的可取值範圍是[x[i-1]+1:r+1]。由此可以看出,在用回溯法時,可以用一棵樹表示其   

解空間。該解空間樹中各結點的度隨x的不同取值而變化。   

設x[1~n]是從小到大排列的n種郵票面值。    

首先x[1]=1 是毫無疑問的,否則最小郵資 1 無法支付。    

面值為1的郵票構成的最大連續區間只能是[1,m] (只貼1張就是1……m張郵票全貼滿是m)。   

如果這時候再給我們第二種面額的郵票。因為它需要比x[1]大,所以最少只能是2;   

又因為單純由x[1]支付的郵資區間最大只到m,再往上就出現了斷檔,所以x[2]最大也只能取m+1   

(如果再大的話,m+1這個值就無法正常表示)    

同樣的道理,假設前面k種郵票面值都已經有了,並且能構成[1, r]的連續郵資區間,   

那麼第k+1種郵票的面值必須滿足:    

x[k]+1 <= x[k+1] <= r+1    

演算法就是要找到這麼多種可能情況下的最優方案   

對於連續郵資問題,用n元組x[1:n]表示n種不同的郵票面值,並約定它們從小到大排列。   

x[1] = 1是惟一的選擇。在未確定剩餘其它n-1種郵票面值的情況下,只用x[1]一種郵票面值,   

所能得到的最大連續郵資區間是[1:m]。接下來,確定x[2]的取值範圍,很明顯,   

x[2]的值最小可以取到2,因為x[1] 這時已經為1了。那麼最大可以取的值呢?應當是m+1,   

為什麼?因為如果x[2]取m+2,則在這個方案中,郵資m+1不可能取得,   

(這個時候,x[2:n]的任何乙個面值是不能取的,因為就算只取一張,總和也會至少為m+2,   

超過m+1了,反過來,如果只取x[1]面值,則就算把m張全取了,也只能湊到m)。所以,   

在搜尋第2張郵票的時候,搜尋範圍是2 ~ m+2(m+2不剪取)。在一般的情況下,已選定x[1:i-1],   

最大連續郵資區間是[1:r]時,接下來x[i]的可取值範圍是[x[i-1]+1 :r+1]。由此可以看出,   

[x[i-1]+1 :r+1](為限界函式)在用回溯法解連續郵資問題時,可用樹表示其解空間,   

該解空間樹中各結點的度隨x的不同取值而變化。   

現在可以大致畫出該解空間樹的結構。   

現在的關鍵問題是,如何確定x3的取值範圍?轉換一下就是如何求得在x[1],x[2]面值確定的情況下,   

用不超過m張郵票,所得到的最大連續郵資區間是多少?如果每乙個這樣的問題能夠確定,   

則解空間樹已經構造好了(每乙個的兒子結點為r-x[i-1]),我們就可以通過深度優先遍歷的方式得到最佳面值設計   

(每到達葉結點見比較更新)。   

現在來看在x[1],x[2]面值確定的情況下,用不超過m張郵票,所得到的最大連續郵資區間是多少?   

最大連續郵資區間總是以1作為起點的,所以我們用max來表示這個最大值,顯然,max至少可以取到m,   

因為即使不用x[2]面值,只用x[1]面值的情況下,所能得到的最大值就已經是m了。現在來看在x[1],   

x[2]面值確定的情況下,用不超過m張郵票,max+1能不能取到?    

假設在拼湊的過程中,x[2]面值的郵票取t張,則t>=0,t<=(max+1) /x[2] && t<=m   

現在x[2]面值的郵票張數已經確定,原問題轉化為另乙個子問題,即,在x[1]面值確定的情況下,   

用不超過m-t張郵票,max+1 - t*x[2] 能不能取到? 。。。。。。   

到這一步就很容易理解了,因為x[1] 面值為1,所以如果要拼湊的值max+1- t*x[2] <= m-t的話,   

則只用取max+1- t*x[2]張就可以拉。相反,如果max+1- t*x[2] > m-t的話,則是不可以滿足的。   

在下面的回溯法描述中,遞迴函式backtrack實現對整個解空間的回溯搜尋。   

maxvalue記錄當前已經找到的最大連續郵資區間,bestx是相應的當前最優解。   

陣列y用來記錄當前已經選定的郵票面值x[1:i]能貼出各種郵資所需的最少郵票數。   

也就是說,y[k]是用不超過m張面值為x[1:i]的郵票,貼出郵資k所需的最少郵票張數。   

在函式backtrack中,   

當i>n時,表示演算法已經搜尋到乙個葉結點,得到乙個新的郵票面值設計方案x[1:n]。如果該方案能貼出的   

最大連續郵資區間大於當前已經找到的最大連續郵資區間maxvalue,則更新當前最優值maxvalue和相應的最優解。   

當i <= n時,當前擴充套件結點z是解空間中的乙個內部結點,在該結點處x[1:i-1]   

能貼出的最大最大郵資區間為r-1.因此在結點z處x[i]的可取範圍是x[i-1]+1:r,   

從而,結點z有r-x[i-1]個兒子結點。演算法對當前擴充套件結點z的每乙個兒子結點,   

以深度優先的方式遞迴地對相應子樹進行搜尋   

解空間是多叉樹,孩子接點個數是每層都在變化的   

#include#includeusing namespace std;

class stamp

;

void stamp::backtrack(int i,int r)

return;

} int *z=new int[maxl+1];

for(k=1;k<=maxl;k++)

z[k]=y[k];

//保留資料副本,以便返回上層時候能夠恢復資料

//以上都是處理第i-1層及其之上的問題

for(j=x[i-1]+1;j<=r;j++) //在第i層有這麼多的孩子結點供選擇

delete z;

}

int maxstamp(int n,int m,int bestx){

stamp x;

int maxint=32767;

int i,maxl=1500;

x.n=n;

x.m=m;

x.maxvalue=0;

x.maxint=maxint;

x.maxl=maxl;

x.bestx=bestx;

x.x=new int [n+1];

x.y=new int [maxl+1];

for(i=0;i<=n;i++)

x.x[i]=0;

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

x.y[i]=maxint;

x.x[1]=1;

x.y[0]=0;

x.backtrack(2,1);

cout<<"當前最優解:";

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

cout<>n;

cout<<"請輸入每張信封最多允許貼的郵票數:";

cin>>m;

bestx=new int[n+1];

for(int i=1;i<=n;i++)

bestx[i]=0;

cout<<"最大郵資:"<

連續郵資問題

王曉東老師編著的 計算機演算法設計與分析 5.12 節以 連續郵資問題 為例展示了回溯法的應用。講解比較簡略,對於搜尋出一張新的郵票面值後如何更新最大連續郵資區間這一點沒有過多的說明。以下是自己對於這一節學習的一點筆記。實際上,關於剛才所說的更新最大連續郵資區間的方法,可以歸結到一種 等價類 的思想...

連續郵資問題

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

連續郵資問題

假設國家發行了n種不同面值的郵票,並且 規定每張信封上最多隻允許貼m張郵票。連 續郵資問題要求對於給定的n和m的值,給出 郵票面值的最佳設計,在1張信封上可貼出 從郵資1開始,增量為1的最大連續郵資區 間。noip99 例如,當n 2 m 3時,如果面值分別為1 4,則在l 6之間的每乙個郵資值都能...