《實用演算法的分析與程式設計》的讀書筆記(第2天) (轉)

worldblog發表於2007-12-13
《實用演算法的分析與程式設計》的讀書筆記(第2天) (轉)[@more@]遞迴 第20頁

[例1]劃分問題 設s是一個具有n個元素的集合s 下列條件的子集合sl,s z,·。,s k: 1.si 56呼 (al,a z,·。,a。),現將s集合劃分成K個滿足 2.S;門Sj=69 ’ 3.S1廖S 2LJ S 3LJ·.·廖Sn=S · (1毒i,j毒k,i,6j) 則稱s n,s z,…,s n是s的一個劃分。它相當於把s集合中的n個元素a n·.·a n放人k 個無標號的盒子中,使得沒有一個盒子為空。試確定n個元素a1…an放人k個無標號盒的劃分數s(n,k)。

分析:

設n個元素a n…a n故人k個無標號盒的劃分數為s(n,k)。在過程中,有兩種互不相容的情況: 1.設(a n)是k個子集中的一個子集,於是把(a n…a n—1)劃分為k一1子集有s(n一 1,k—1)個劃分數; 2.如果(an)不是k個子集中的一個,即an必與其它的元素構成一個子集。首先把 (a,,…,an—1)劃分成k個子集,這共有s(n一1,k)種劃分方式。然後再把an加入到k 個子集中的一個子集中去,這有k種加入方式。對於每一種加入方式,都使集合劃分為k 個子集,因此由乘法原理知,劃分數共有 k*s(n一1,k)。 應用加法原理於上述兩種情況,得出I a,,…,an)劃分為k個子集的劃分數: s(n,k)=s(n一1,k一1)十k*s(n一1,k) (n>1,k>=1)


現在考慮s(n,k)的邊界條件:1). s(n,0)=0; 當k>n時s(n,k)=0;


2).s(n,1)=1; s(n,n)=1;


由此得到遞迴定義:


s(n,k)=s(n一1,k一1)十k*s(n一1,k) (n>k,k>=1) s(n,k)=o (n<k)或(k=0<n) s(n,k)=1 (k=1)或(k=n)


題解:


#include
int s(int n,int k)
{
 int sum;
 if(n  else if(k==1||k==n) sum=1;
  else sum=s(n-1,k-1)+k*s(n-1,k);
  return sum;
}
int main()
{
 int n,k;
  while(cin>>n>>k){
   int sum=s(n,k);
  cout<  }
  return 1;
}


分治法 第22頁

所謂分治策略:既將原問題分成n個規模較小而結構與原問題相似的子問題。遞迴地解這些子問題,然後合併其結果就得到原問題的解。n=2 時的分治法又稱二分法。


分治法在每一層遞迴上都有三個步驟: 分解:將原問題分解成一系列子問題;
解決:遞迴地解各子問題。若子問題足夠小,則直接解
合併:將子問題的結果合併成原問題的解;
[例1I合併排序
  對序列A[1],A[2],……,A[n]進行合併排序。
演算法分析:
  合併排序的演算法就是二分法。
  分解:將n個元素各含rn/2。1(或Ln/2J)個元素的子序列
  解決:用合併排序法對兩個子序列遞迴地排序;
  合併:合併兩個已排序的子序列以得到排序結果。


前面在貪心法中用到的 merge(int p,int q,int r)與merge(int p,int r)就


是典型的二分排序法。這裡不再重複。


 


列舉法 第31頁

所謂列舉法,指的是從可能的解的集合中一一列舉各元素, 用題目給定的檢驗條件判定哪些是無用的,哪些是有用的。能使命題成立,即為其解.


一般來說,如果預先對問題確定瞭解的個數以及各個解的值域。我們則可以利用ror語句和條件判斷語句逐步求解或證明. 
如果我們無法預先確定解的個數或各解的值域,我們只能採用隱式圖搜尋的演算法求
解,例如回溯法等。由於回溯搜尋,每個可能解的列舉一般不止一次,因此在相同的檢驗
條件下,回溯法要比枚學法費時一些。
[例0填寫運算子
輸入任意5個數 x1,x2,x3,x4,x5
每相鄰兩個數間填上一個運算子。在填入四個運算子後,使得值為一個指定值
y(y由鍵盤輸入)。求出所有滿足條件的表示式。分析:為了解決運算的優先順序問題,我們設定如下變數:
  f——減法標誌。減法運算時,置f=一1,否則f=1;
  q——若當前運算子為十(一)時,q存貯運算子的左項值;
若當前運算子為*(/)時,q存貯兩數乘(除)後的結果;
  p——累加器。每填一個算符p=p十f *q。


然後四重for迴圈,解決問題。這是一道典型的列舉題,運算
量很大。具體還是比較簡單,故不再轉化。



列舉法有其計算量大的缺點,但是如果能夠排除那些明顯不屬
於解集的元素,在區域性地方使用列舉法,其效果會十分顯著。


[例2]時針問題
  在圖1.5。1所示的3×3陣列中有9個時鐘,我們的目標是旋轉時鐘指標,使所有
時鐘的指標都指向12點。允許旋轉時鐘指標的方法有9種,每一種移動用一個數字號(1,
2,…,9)表示。  圖1.5—2示出了9個數字號與相應的受控制的時鐘,這些時鐘在圖中以灰
色標出,其指標將順時針旋轉90度。


輸入資料:
輸入9個數碼,這些數碼給出9個時鐘針的初始位置。數碼與時刻
的對應關係為0--&gt12點,1->3點,2--&gt6點,3--&gt9點。
例:3 3 0 2 2 2 2 1 2
輸出資料:
輸出一個最短的移動序列(數字)
例:5 8 4 9


題解:


#include
int clocks[9],map[9];
void init()
{
 int i;
  for(i=0;i<9;i++){
   cin>>clocks[i];
  clocks[i]=(4-clocks[i])%4;
  }
}
int order(int k)
{
 int c=k;
  while(c>4)c-=4;
  while(c<0)c+=4;
  return c;
}
void clock()
{
 init();
  int i; bool flag=false;
  for(map[0]=0;map[0]<=3;map[0]++)
   for(map[1]=0;map[1]<=3;map[1]++)
   for(map[2]=0;map[2]<=3;map[2]++){
   map[3]=order(clocks[0]-map[0]-map[1]);
  map[5]=order(clocks[2]-map[1]-map[2]);
  map[4]=order(clocks[1]-map[0]-map[1]-map[2]);
  map[6]=order(clocks[3]-map[0]-map[3]-map[4]);
  map[8]=order(clocks[5]-map[2]-map[4]-map[5]);
  map[7]=order(clocks[7]-map[6]-map[8]-map[4]);
  if(clocks[6]==(map[3]+map[6]+map[7])%4&&
   clocks[4]==(map[4]+map[0]+map[2]+map[6]+map[8])%4&&
  clocks[8]==(map[5]+map[7]+map[8])%4){
  for(i=0;i<9;i++)
   if(map[i]!=0)cout<  cout<  flag=true;
  }
  }
  if(!flag)cout<}
int main()
{
 clock();
  return 1;
}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992875/,如需轉載,請註明出處,否則將追究法律責任。

相關文章