算法設計與分析(李春保)練習題答案v1


 

 

1.11 概論

 

1.1.1練習題

 

1.下列於算法的說法中確的有()。

 

Ⅰ.求解某一類題的算法是唯一

 

Ⅱ.算法必須在限步操作之后停

 

Ⅲ.算法的每一操作必須是明確,不能有歧義或義模糊

 

Ⅳ.算法執行后定產生確定的結

 

A. 1 B.2 C.3 D.4

 

2. T(n)表示當入規模為n時的算法效率,以下法效率最優的是)。

 

 

A.T(n)= T(n-1)+1T(1)=1 C.T(n)= T(n/2)+1T(1)=1

 

B.T(n)= 2n

 

D.T(n)=3nlog2n

 

 

3.什么算法?算法有哪特征?

 

4.判斷一個2的正整數n是否為素數的方有多種,給出兩算法,說明其中一種算法更好的由。

 

5.證明下關系成立:

 

110n-2n=(n)

 

22=(2)

 

6.證明O(f(n))+O(g(n))=O(max{f(n)g(n)})

 

7.個含nn>2)個數的數組a,判斷中是否存在出現數超過所有元素半的元素。

 

8.一個符串采用string 象存儲,設計一算法判斷該字符是否為回文。

 

9.有一個整序列,設計一個算法判斷其中是存在兩個元素和好等於給定的整k

 

10.有兩個整序列,每個整數序列中所有元素不相同。設計一算法求它們的公共元素,要求不使STL 的集合算

 

11.正整nn>1)可以寫成質數的乘積式,稱為整數的質因數分解。如,12=2*2*318=2*3*311=11。設計一個法求n這樣分解后各個質因數現的次數,采vector 向量存結果。

 

12.有一個數序列,所有元素均不相同,設一個算法求相差小的元素對的個數。如序列4123的相差最小的元素對的數是3,其元素對是(12),(23),34)。

 

13.有一個map<stringint>容器,其中已經存放了多元素。設計一算法求出其中重復的value 返回重復value 數。

 

14.做第10,采用map 容器存放終結果。

 

15.假設有一nn>1)個元素的stack<int>st,設計一個法出棧從棧頂到棧底的第k1kn)個元素,他棧元素不變。

 

 

 

 

算法設計

 

1.1.2練習題參考答

 

1.答:由於法具有有窮性、確定性和輸出性因而確,而解決某一類問題的算法不定是唯一的。答C

 

2.答:A的時間復雜度為O(n)。選項B的時間復雜度為O(n)。選項C的時間復雜度為O(log2n)。選項D 的時復雜度為O(nlog2n)。答案為C

 

3.答:算法求解問題的一系列計算步驟。算具有有限性、確性、可行性、輸入性和輸出性5 重要特征。

 

4.答:種算法如下:

 

#include <stdio.h>

 

#include <math.h>

 

boolisPrime1(intn)//1

 

{for (int i=2;i<n;i++)

 

if (n%i==0)

 

return false;

 

return true;

 

}

 

boolisPrime2(intn)//2

 

{for (int i=2;i<=(int)sqrt(n);i++)

 

if (n%i==0)

 

return false;

 

return true;

 

}

 

voidmain()

 

{int n=5;

 

printf("%d,%d\n",isPrime1(n),isPrime2(n));

 

}

 

方法1 的時間復度為O(n),方法2的時間復雜度n,所以2 更好。5.答:1)當n 足夠大(10n-2n)/( n)=10所以10n-2n=(n)

 

22=2*2=(2)

 

6.明:對於任意f1(n)O(f(n)),存在正c1 和正常數n1使得對所有nn1f1(n)c1f(n)

 

類似地,對於任g1(n)O(g(n)),存在正常數c2 自然數n2,使得所有nn2g1(n)c2g(n)

 

c3=max{c1c2}n3=max{n1n2}h(n)= max{f(n)g(n)}

 

則對所有的nn3,有:

 

f1(n) +g1(n)c1f(n) +c2g(n)c3f(n)+c3g(n)=c3(f(n)+g(n))

 

c32max{f(n)g(n)}=2c3h(n)=O(max{f(n)g(n)})

 

7.先將a 中元素遞排序,再求出現數最多的次數maxnum,最后判斷否滿足條件。對應的序如下:

 

#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

2

 

 

 

 

1

 

概論

 

 

boolsolve(inta[],intn,int&x)

 

 

{sort(a,a+n);

 

int maxnum=0;

 

int num=1;

 

int e=a[0];

 

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

 

{if (a[i]==e)

 

{num++;

 

//遞增排序

 

//出現次數最多的

 

 

if (num>maxnum)

 

{maxnum=num;

 

x=e;

 

}

 

}

 

else

 

{e=a[i];

 

num=1;

 

}

 

}

 

if (maxnum>n/2)

 

return true;

 

else

 

return false;

 

}

 

voidmain()

 

{int a[]={2,2,2,4,5,6,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

int x;

 

if (solve(a,n,x))

 

printf("出現次數過所有元素一半的元素為%d\n",x); else

 

printf("不存在出次數超過所有元素一半的元素\n");

 

}

 

上述程序的執行果如圖1.1 所示

 

 

1.1

 

程序執行結果

 

 

8.解:用前后字符判斷法,對應的程序下:#include <iostream>

 

#include <string>

 

using namespace std;

 

boolsolve(stringstr)//斷字符串str為回文{int i=0,j=str.length()-1;

 

while (i<j)

 

{if (str[i]!=str[j])

 

return false;

 

3

 

 

 

 

算法設計

 

i++; j--;

 

}

 

return true;

 

}

 

voidmain()

 

{cout << "結果"<<endl;

 

string str="abcd";

 

cout << "" << str<<(solve(str)?"是回":"不是回文")<< endl; string str1="abba";

 

cout << "" << str1<<(solve(str1)?"是回文":"回文")<<endl;}

 

上述程序的執行果如圖1.2所示

 

 

1.2

 

程序執行結果

 

 

9.解:a 中元素遞增序,然后從兩端始進行判斷。對的程序如下:#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

boolsolve(inta[],intn,intk)

 

 

{sort(a,a+n);

 

int i=0, j=n-1;

 

while (i<j)

 

{if (a[i]+a[j]==k)

 

return true;

 

//遞增排序

 

//區間中存在兩個者以上元素

 

 

else if (a[i]+a[j]<k)

 

i++;

 

else

 

j--;

 

}

 

return false;

 

}

 

voidmain()

 

{int a[]={1,2,4,5,3};

 

int n=sizeof(a)/sizeof(a[0]);

 

printf("求解結果\n");

 

int k=9,i,j;

 

if (solve(a,n,k,i,j))

 

printf("存在: %d+%d=%d\n",a[i],a[j],k);else

 

printf("不存在個元素和為%d\n",k);

 

int k1=10;

 

if (solve(a,n,k1,i,j))

 

printf("存在: %d+%d=%d\n",a[i],a[j],k1);

 

4

 

 

 

 

1

 

概論

 

 

else

 

 

printf("

 

不存在兩個元素和%d\n",k1);

 

 

}

 

上述程序的執行果如圖1.3 所示

 

 

1.3

 

程序執行結果

 

 

10.解:采用集合set<int>儲整數序列,集中元素默認是遞增排序的,再用二路歸並算法求它的交集。對應的序如下:

 

#include <stdio.h>

 

#include <set>

 

using namespace std;

 

 

voidsolve(set<int>s1,set<int>s2,set<int>&s3){set<int>::iteratorit1,it2;

 

it1=s1.begin(); it2=s2.begin();

 

while (it1!=s1.end()&& it2!=s2.end())

 

{if (*it1==*it2)

 

{s3.insert(*it1);

 

++it1; ++it2;

 

}

 

else if (*it1<*it2)

 

++it1;

 

else

 

++it2;

 

}

 

}

 

//s3

 

 

voiddispset(set<int>s)

 

{set<int>::iteratorit;

 

//集合的元素

 

 

for (it=s.begin();it!=s.end();++it)

 

printf("%d ",*it);

 

printf("\n");

 

}

 

voidmain()

 

{int a[]={3,2,4,8};

 

int n=sizeof(a)/sizeof(a[0]);

 

set<int> s1(a,a+n);

 

int b[]={1,2,4,5,3};

 

int m=sizeof(b)/sizeof(b[0]);

 

set<int> s2(b,b+m);

 

set<int> s3;

 

solve(s1,s2,s3);

 

printf("求解結果\n");

 

printf("s1: ");dispset(s1);

 

 

5

 

 

 

 

算法設計

 

 

printf("

 

printf("

 

s2: "); dispset(s2);

 

s3: "); dispset(s3);

 

 

}

 

上述程序的執行果如圖1.4 所示

 

 

1.4

 

程序執行結果

 

 

11.解:正整數n,從i=2 始查找其質因數ic 記錄質因數i 的次數,當找到這樣質因數后將(iic)作為一個素插入到vector v 中。最后輸出v對應的算法如下:

 

#include <stdio.h>

 

#include <vector>

 

using namespace std;

 

 

struct NodeType

 

{int p;

 

int pc;

 

//vector向量元素 //質因數

 

//質因數出現次數

 

 

};

 

voidsolve(intn,vector<NodeType>&v)//n{int i=2;

 

int ic=0;

 

NodeType e;

 

do

 

{if (n%i==0)

 

{ic++;

 

n=n/i;

 

}

 

else

 

{if (ic>0)

 

{e.p=i;

 

e.pc=ic;

 

v.push_back(e);

 

}

 

ic=0;

 

i++;

 

}

 

} while (n>1 || ic!=0);

 

}

 

voiddisp(vector<NodeType>&v)//v

 

{vector<NodeType>::iteratorit;

 

for (it=v.begin();it!=v.end();++it)

 

printf("質因數%d出現%d\n",it->p,it->pc);

 

}

 

6

 

 

 

 

1

 

概論

 

 

voidmain()

 

{vector<NodeType>v;

 

int n=100;

 

printf("n=%d\n",n);

 

solve(n,v);

 

disp(v);

 

}

 

上述程序的執行果如圖1.5 所示

 

 

1.5

 

程序執行結果

 

 

12.解:增排序,再求相鄰元素差,比較最小元素差,累最小元素差的個數。對應的程序下:

 

#include <iostream>

 

#include <algorithm>

 

#include <vector>

 

using namespace std;

 

 

intsolve(vector<int>&myv)

 

//myv中相最小的元素對的個數

 

 

{sort(myv.begin(),myv.end());//遞增排序

 

int ans=1;

 

int mindif=myv[1]-myv[0];

 

for (int i=2;i<myv.size();i++)

 

{if (myv[i]-myv[i-1]<mindif)

 

{ans=1;

 

mindif=myv[i]-myv[i-1];

 

}

 

else if (myv[i]-myv[i-1]==mindif)

 

ans++;

 

}

 

return ans;

 

}

 

voidmain()

 

{int a[]={4,1,2,3};

 

int n=sizeof(a)/sizeof(a[0]);

 

vector<int> myv(a,a+n);

 

cout << "相差最的元素對的個數:" << solve(myv) <<endl; }

 

上述程序的執行果如圖1.6

 

 

7

 

 

 

 

 

1.6


算法設計

 

程序執行結果

 

 

13.解:對於map<stringint>容器mymap,設計另外一map<intint>容器tmap將前者的value 后者的關鍵字。mymap,累計tmap 中相同關鍵字次數。一個參考程序及其輸結果如下

 

#include <iostream>

 

#include <map>

 

#include <string>

 

using namespace std;

 

voidmain()

 

{map<string,int>mymap;

 

mymap.insert(pair<string,int>("Mary",80));

 

mymap.insert(pair<string,int>("Smith",82));

 

mymap.insert(pair<string,int>("John",80));

 

mymap.insert(pair<string,int>("Lippman",95));

 

mymap.insert(pair<string,int>("Detial",82));

 

map<string,int>::iteratorit;

 

map<int,int> tmap;

 

for (it=mymap.begin();it!=mymap.end();it++)

 

tmap[(*it).second]++;

 

map<intint>::iteratorit1;

 

cout << "求解結"<<endl;

 

for (it1=tmap.begin();it1!=tmap.end();it1++)

 

cout << "" << (*it1).first<< ":" << (*it1).second<< "\n";

 

}

 

上述程序的執行果如圖1.7 所示

 

 

1.7

 

程序執行結果

 

 

14.解:采用map<intint>容器mymap 存放解結果,第一個量存放質因數,二個分量存放質數出現次數。對的程序如下:

 

#include <stdio.h>

 

#include <map>

 

using namespace std;

 

voidsolve(intn,map<int,int>&mymap)//n分解

 

{int i=2;

 

int ic=0;

 

do

 

 

{

 

if (n%i==0)

 

{ic++;

 

n=n/i;

 

}

 

 

8

 

 

 

 

1

 

概論

 

 

else

 

{if (ic>0)

 

mymap[i]=ic;

 

ic=0;

 

i++;

 

}

 

} while (n>1 || ic!=0);

 

}

 

voiddisp(map<int,int>&mymap)//mymap

 

{map<int,int>::iteratorit;

 

for (it=mymap.begin();it!=mymap.end();++it)

 

printf("質因數%d出現%d\n",it->first,it->second);

 

}

 

voidmain()

 

{map<int,int>mymap;

 

int n=12345;

 

printf("n=%d\n",n);

 

solve(n,mymap);

 

disp(mymap);

 

}

 

上述程序的執行果如圖1.8

 

 

1.8

 

程序執行結果

 

 

15.解:棧容不能順序遍歷,為此創建一個臨tmpst 棧,將st k個元素出棧並進棧到tmpst 再出棧tmpst一次得到第k 個元素最后將棧tmpst 有元素出棧並進棧到st 中。對應程序如下:

 

#include <stdio.h>

 

#include <stack>

 

using namespace std;

 

 

intsolve(stack<int>&st,intk)

 

{stack<int> tmpst;

 

int e;

 

for (int i=0;i<k;i++)

 

{e=st.top();

 

st.pop();

 

tmpst.push(e);

 

}

 

e=tmpst.top();

 

tmpst.pop();

 

while (!tmpst.empty())

 

{st.push(tmpst.top());

 

tmpst.pop();

 

//k個元

 

//出棧stk素並進tmpst//求第k個元素

 

//tmpst的所有素出棧並進棧st9

 

 

 

 

 

}

 

return e;

 

}

 

voiddisp(stack<int>&st)

 

{while (!st.empty())

 

{printf("%d ",st.top());

 

st.pop();

 

}

 

printf("\n");

 

}

 

voidmain()

 

{stack<int> st;


算法設計

 

//st

 

 

printf("進棧元素1,2,3,4\n");st.push(1);

 

st.push(2);

 

st.push(3);

 

st.push(4);

 

int k=3;

 

int e=solve(st,k);

 

printf("出棧第%d元素是: %d\n",k,e);printf("st中元素棧順序: "); disp(st);

 

}

 

上述程序的執行果如圖1.9 所示

 

 

1.9

 

程序執行結果

 

 

1.22 遞歸算法設計技術

 

1.2.1練習題

 

1.什么直接遞歸和間接歸?消除遞歸一要用到什么數據構?2.分析下程序的執行結

 

#include <stdio.h>

 

voidf(intn,int&m)

 

{if (n<1) return;

 

else

 

 

{

 

}

 

printf("調用f(%d,%d),n=%d,m=%d\n",n-1,m-1,n,m);n--; m--;

 

f(n-1,m);

 

printf("調用f(%d,%d):n=%d,m=%d\n",n-1,m-1,n,m);

 

10

 

 

 

 

1

 

概論

 

 

}

 

voidmain()

 

{int n=4,m=4;

 

f(n,m);

 

}

 

3.采用接推導方法求解下遞歸方程:T(1)=1

 

T(n)=T(n-1)+nn>1

 

4.采用征方程方法求解下遞歸方程:H(0)=0

 

H(1)=1

 

H(2)=2

 

H(n)=H(n-1)+9H(n-2)-9H(n-3)n>2 5.采用歸樹方法求解以遞歸方程:T(1)=1

 

T(n)=4T(n/2)+nn>1

 

6.采用方法求解以下題遞歸方程。

 

 

T(n)=1

 

n=1

 

 

T(n)=4T(n/2)+nn>1

 

7.分析斐波那契f(n)間復雜度。

 

8.的首項a1=0,后奇數項和偶數項計算公式分別為a2n=a2n-1+2a2n+1=a2n- 1+a2n-1,寫出計數列第n項的遞歸算法。

 

9.於一個采用字符數組存放的字str,設計一個遞歸算法求其符個數(長)。

 

10.於一個采用字符組存放的字符串str,設計一個歸算法判斷str 否為回文。

 

11.不帶頭結點的單鏈表L,設計一遞歸算法正序輸所有結點值。

 

12.不帶頭結點的單鏈表L,設計一遞歸算法逆序輸所有結點值。

 

13.對於不頭結點的非空單L,設計一個遞歸算法返回最值結點的地址(設這樣的結點唯)。

 

14.對於不頭結點的單鏈表L,設計一個遞歸算法返回第一個x 的結點的地址,沒有這樣的點時返回NULL

 

15.不帶頭結點的單鏈表L,設計一遞歸算法刪除第個值為x 的結點

 

16.假設叉樹采用二叉鏈儲結構存放,結點值為int 類型設計一個遞歸算法求二叉樹bt 中所有子結點值之和。

 

17.假設叉樹采用二叉鏈儲結構存放,結點值為int 類型設計一個遞歸算法求二叉樹bt 中所有點值大於等於k 結點個數。

 

18.假設二叉采用二叉鏈存儲結構存放,所有點值均不相同,計一個遞歸算法求值為x 的結點層次(根結點的次為1),沒有找到樣的結點時返回0

 

11

 

 

 

 

算法設計

 

1.2.2練習題參考答

 

1.答:一個f 數定義中直接調用f 函數自己,為直接遞歸。一f 函數定義中調g 函數,而g 數的定義中調用f 數,稱為間接遞。消除遞歸一般用棧實現。

 

2.答:歸函數f(nm)n是非引用參數m是引用參數,所以遞歸函數的態為n)。程序執結果如下:

 

調用f(3,3),n=4,m=4

 

調用f(1,2),n=2,m=3

 

調用f(0,1),n=1,m=2

 

調用f(2,1),n=3,m=2

 

3.解:T(n)的過程如

 

T(n)=T(n-1)+n=[T(n-2)+n-1)]+n=T(n-2)+n+(n-1)

 

=T(n-3)+n+(n-1)+(n-2)

 

=

 

=T(1)+n+(n-1)++2

 

=n+(n-1)+ ++2+1=n(n+1)/2=O(n)

 

 

4.整數一個常系的線性齊次遞推,用x

 

n-33232

 

代替H(n),有x=x+9x-9x

 

 

x-x-9x+9=x(x-9)-(x-9)=(x-1)(x-9)=(x-1)(x+3)(x-3)=0得到r1=1r2=-3r3=3則遞歸方程的通為:H(n)=c1+c2(-3)+c33

 

代入H(0)=0c1+c2+c3=0

 

代入H(1)=1c1-3c2+3c3=1

 

 

代入H(2)=2c1+9c2+9c3=2

 

求出:c1=-1/4c2=-1/12c3=1/3H(n)=c1+c2(-3)+c33=(

 

n1

 

4

 

+ 13

 

1

 

 

5.解:構造遞歸樹如圖1.10 所示,第1 問題規模為n,第2 的層的子問問題規模為n/2此類推,當展開k+1層,其規模為n/2=1,所以遞歸的高度為log2n+1

 

1層有1個結點其時間為n,第24個結點,其間為4(n/2)=2n,依次類推,第k 層有4點,每個子問題規模為n/2其時間為4(n/2)=2n。葉子結點個數為n 個,其時間為n將遞歸樹每一層時間加起來,可

 

logn

T(n)=n+2n++ 2n++n𝑛2=O(n)

 

 

12

 

 

 

 

1

 

概論

 

 

n

 

n

 

 

(n/2)

 

(n/2)

 

(n/2)

 

(n/2)

 

2n

 

 

高度h log2n+1

 

(n/2)

 

 

1

 

(n/2)

 

 

1

 

(n/2)

 

 

1

 

(n/2)

 

 

1

 

2n

 

n

 

 

1.10一棵遞歸

 

6.解:用主方法求解,a=4b=2f(n)=n

logalog4

因此,𝑛=𝑛=n,它與f(n)大,滿足主定理的情況(2),所以T(n)=O( loga

𝑛log2n)=O(nlog2n)

 

7.解:求斐波那契f(n)時間為T(n),有以下遞推式:

 

T(1)=T(2)

 

 

T(n)=T(n-1)+T(n-2)+1

 

n>2

 

 

其中,T(n)式中1表示一次加法運算的時間。

 

不妨先求T1(1)=T1(2)=1T1(n)=T1(n-1)+T1(n-2),按《教程》例2.14的方法可以求

 

 

出:

 

 

T1(n)=

 

1

 

5

 

15

 

 

1

 

5

 

15

 

115=

 

 

T(n)=T1(n)+1

 

1

 

5

 

15

 

+1=O(φ),其中φ=

2

 

 

8.解:f(m)計算數列第m項值。

 

m 為偶數時不妨設m=2n2n-1=m-1,所f(m)=f(m-1)+2

 

m 為奇數時不妨設m=2n+12n-1=m-22n=m-1,所以有f(m)=f(m-2)+f(m-1)-1

 

對應的遞歸算法下:

 

intf(int m)

 

{if (m==1) return0;

 

if (m%2==0)

 

return f(m-1)+2;

 

else

 

return f(m-2)+f(m-1)-1;

 

}

 

9.解:f(str)返回字符串str的長度,其遞歸型如下:

 

 

f(str)=0

 

f(str)=f(str+1)+1

 

*str='\0'

 

其他情況

 

 

對應的遞歸程序下:

 

 

13

 

 

 

 

 

#include <iostream>

 

using namespace std;

 

intLength(char*str)

 

{if (*str=='\0')

 

return 0;

 

else

 

return Length(str+1)+1;

 

}

 

voidmain()

 

{char str[]="abcd";


算法設計

 

//str的字個數

 

 

cout << str << "長度:"<<Length(str) << endl; }

 

上述程序的執行果如圖1.11 所示

 

 

1.11

 

程序執行結果

 

 

10.f(strn)返回含n 個字符的字str是否為回文,其遞歸模型下:

 

 

f(strn)=true

 

f(strn)=flase

 

f(strn)=f(str+1n-2)

 

n=0 或者n=1 str[0]str[n-1]其他情況

 

 

對應的遞歸算法下:

 

#include <stdio.h>

 

#include <string.h>

 

boolisPal(char*str,intn)//str判斷算法{if (n==0 ||n==1)

 

return true;

 

if (str[0]!=str[n-1])

 

return false;

 

return isPal(str+1,n-2);

 

}

 

voiddisp(char*str)

 

{int n=strlen(str);

 

if (isPal(str,n))

 

printf("%s是回\n",str);

 

else

 

printf("%s不是\n",str);

 

}

 

voidmain()

 

{printf("結果\n");

 

disp("abcba");

 

disp("a");

 

disp("abc");

 

}

 

14

 

 

 

 

1

 

概論

 

 

上述程序的執行果如圖1.12 所示

 

 

1.12

 

程序執行結果

 

 

11.f(L)正序輸出單鏈表L 的所有結值,其遞歸模型下:

 

 

f(L)不做任何事

 

f(L)輸出L->data; f(L->next);對應的遞歸程序下:#include "LinkList.cpp"

 

L=NULL

 

LNULL

 

//包含單鏈表的基運算算法

 

 

voiddispLink(LinkNode*L)//序輸出所有結點值{if (L==NULL)return;

 

else

 

{printf("%d ",L->data);

 

dispLink(L->next);

 

}

 

}

 

voidmain()

 

{int a[]={1,2,5,2,3,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

LinkNode *L;

 

 

CreateList(L,a,n);printf("正向L: ");dispLink(L); printf("\n");

 

Release(L);

 

//a[0..n-1]不帶頭結點的單鏈表 //銷毀單鏈表

 

 

}

 

上述程序的執行果如圖1.13 所示

 

1.13

 

程序執行結果

 

 

12.f(L)逆序輸出單鏈表L 的所有結值,其遞歸模型下:

 

 

f(L)不做任何事

 

f(L)f(L->next);輸出L->data對應的遞歸程序下:

 

#include "LinkList.cpp"

 

voidRevdisp(LinkNode*L)

 

{if (L==NULL)return;

 

L=NULL

 

LNULL

 

//包含單鏈表的基運算算法//輸出所有結點值

 

15

 

 

 

 

 

else

 

{Revdisp(L->next);

 

printf("%d ",L->data);

 

}

 

}

 

voidmain()

 

{int a[]={1,2,5,2,3,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

LinkNode *L;

 

CreateList(L,a,n);

 

printf("反向L: ");

 

Revdisp(L); printf("\n");

 

Release(L);

 

}

 

上述程序的執行果如圖1.14 所示

 

1.14


算法設計

 

程序執行結果

 

 

13.f(L)返回單鏈表L 中值最大結點地址,其遞歸模如下:

 

 

f(L) = L

 

f(L) =MAX{f(L->next)L->data}對應的遞歸程序下:#include "LinkList.cpp"

 

L 只有一個結點其他情況

 

//包含單鏈表的基運算算法

 

 

LinkNode*Maxnode(LinkNode*L)//{if (L->next==NULL)

 

 

return L;

 

else

 

{LinkNode *maxp;

 

maxp=Maxnode(L->next);

 

if (L->data>maxp->data)

 

return L;

 

else

 

return maxp;

 

}

 

}

 

voidmain()

 

{int a[]={1,2,5,2,3,2};

 

//只有一個結點時

 

 

int n=sizeof(a)/sizeof(a[0]);

 

LinkNode *L,*p;

 

CreateList(L,a,n);

 

p=Maxnode(L);

 

printf("最大結點:%d\n",p->data); Release(L);

 

 

16

 

 

 

 

1

 

概論

 

 

}

 

上述程序的執行果如圖1.15 所示

 

1.15

 

程序執行結果

 

 

14.f(Lx)返回單鏈表L 中第一個值x 的結點的地址其遞歸模型如下

 

 

f(Lx) = NULL

 

f(Lx) = L

 

f(Lx) =f(L->nextx) 對應的遞歸程序下:

 

L=NULL

 

LNULL L->data=x 其他情況

 

 

#include "LinkList.cpp"

 

LinkNode*Firstxnode(LinkNode*L,intx) {if (L==NULL)return NULL;

 

if (L->data==x)

 

return L;

 

else

 

return Firstxnode(L->next,x);

 

}

 

voidmain()

 

{int a[]={1,2,5,2,3,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

LinkNode *L,*p;

 

CreateList(L,a,n);

 

int x=2;

 

p=Firstxnode(L,x);

 

printf("結點值: %d\n",p->data);Release(L);

 

}

 

上述程序的執行果如圖1.16 所示

 

//包含單鏈表的基運算算法

 

//第一個值為x的結

 

 

1.16

 

程序執行結果

 

 

15.f(Lx)刪除單鏈表L 中第一個值x 的結點,其遞模型如下:

 

 

f(Lx)不做任何

 

f(Lx)刪除L L=L->nextf(Lx)f(L->nextx)

 

對應的遞歸程序下:

 

L=NULL

 

LNULL L->data=x 其他情況

 

17

 

 

 

 

 

#include "LinkList.cpp"


算法設計

 

//包含單鏈表的基運算算法

 

 

voidDelfirstx(LinkNode*&L,intx)//除單鏈表L中第個值為x{if (L==NULL)return;

 

if (L->data==x)

 

{LinkNode *p=L;

 

L=L->next;

 

free(p);

 

}

 

else

 

Delfirstx(L->next,x);

 

}

 

voidmain()

 

{int a[]={1,2,5,2,3,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

LinkNode *L;

 

CreateList(L,a,n);

 

printf("刪除前L:"); DispList(L);

 

int x=2;

 

printf("刪除第一值為%d的結點\n",x);

 

Delfirstx(L,x);

 

printf("刪除后L:"); DispList(L);

 

Release(L);

 

}

 

上述程序的執行果如圖1.17 所示

 

 

1.17

 

程序執行結果

 

 

16.f(bt)返回二叉樹bt 中所有葉子結值之和,其遞歸型如下:

 

 

f(bt)=0

 

f(bt)=bt->data

 

f(bt)=f(bt->lchild)+f(bt->rchild) 對應的遞歸程序下:#include "Btree.cpp"

 

intLeafSum(BTNode*bt)

 

{if (bt==NULL)return 0;

 

bt=NULL

 

btNULL bt 為葉子結點其他情況

 

//包含二叉樹的基運算算法

 

//bt

 

 

if (bt->lchild==NULL&& bt->rchild==NULL)

 

return bt->data;

 

int lsum=LeafSum(bt->lchild);

 

int rsum=LeafSum(bt->rchild);

 

return lsum+rsum;

 

}

 

voidmain()

 

18

 

 

 

 

1

 

概論

 

 

{

 

BTNode *bt;

 

Int a[]={5,2,3,4,1,6};//序列

 

Int b[]={2,3,5,1,4,6};//序列

 

int n=sizeof(a)/sizeof(a[0]);bt=CreateBTree(a,b,n);//ab構造二叉鏈bt printf("二叉樹bt:");DispBTree(bt);printf("\n"); printf("所有葉子點值之和:%d\n",LeafSum(bt));

 

 

DestroyBTree(bt);

 

//銷毀樹bt

 

 

}

 

上述程序的執行果如圖1.18 所示

 

 

1.18

 

程序執行結果

 

 

17.解:f(btk)返回二叉樹bt 中所有結值大於等於k 點個數,其遞歸模型如下:

 

 

f(btk)=0

 

f(btk)=f(bt->lchildk)+f(bt->rchildk)+1f(btk)=f(bt->lchildk)+f(bt->rchildk)

 

對應的遞歸程序下:

 

#include "Btree.cpp"

 

intNodenum(BTNode*bt,intk)

 

{if (bt==NULL)return 0;

 

int lnum=Nodenum(bt->lchild,k);

 

int rnum=Nodenum(bt->rchild,k);

 

if (bt->data>=k)

 

return lnum+rnum+1;

 

else

 

return lnum+rnum;

 

}

 

voidmain()

 

{BTNode *bt;

 

Int a[]={5,2,3,4,1,6};

 

Int b[]={2,3,5,1,4,6};

 

int n=sizeof(a)/sizeof(a[0]);

 

bt=CreateBTree(a,b,n);

 

bt=NULL

 

btNULL bt->datak其他情況

 

//包含二叉樹的基運算算法//等於k的結個數

 

//ab構造二叉bt

 

 

printf("二叉樹bt:");DispBTree(bt);printf("\n"); int k=3;

 

printf("大於等於%d的結點個數:%d\n",k,Nodenum(bt,k));

 

 

DestroyBTree(bt);

 

//銷毀樹bt

 

 

}

 

上述程序的執行果如圖1.19 所示

 

 

19

 

 

 

 

算法設計

 

 

1.19

 

程序執行結果

 

 

18.解:f(btxh)返回二叉樹btx 結點的層次其中h 表示bt 結點的層次,初始調用時bt 指向根結點,h 1。其遞歸模如下:

 

 

f(btxh)=0

 

f(btxh)=h

 

f(btxh) =l

 

f(btxh) =f(bt->rchildxh+1) 對應的遞歸程序下:

 

bt=NULL

 

btNULL bt->data=xl=f(bt->lchildxh+1)0其他情況

 

 

#include "Btree.cpp"

 

intLevel(BTNode*bt,intx,int h) {//初始調用bt為根,h1

 

if (bt==NULL) return0;

 

if (bt->data==x)

 

return h;

 

//包含二叉樹的基運算算法//叉樹btx

 

//找到x結點,返h

 

 

else

 

{int l=Level(bt->lchild,x,h+1);//在左子樹中

 

 

if (l!=0)

 

return l;

 

else

 

//在左子樹中找到返回其層次l

 

 

return Level(bt->rchild,x,h+1);//回在右子樹的查找

 

 

}

 

}

 

voidmain()

 

{BTNode *bt;

 

Int a[]={5,2,3,4,1,6};

 

Int b[]={2,3,5,1,4,6};

 

int n=sizeof(a)/sizeof(a[0]);

 

bt=CreateBTree(a,b,n);

 

//ab構造二bt

 

 

printf("二叉樹bt:");DispBTree(bt);printf("\n"); int x=1;

 

printf("%d結點的: %d\n",x,Level(bt,x,1));

 

 

DestroyBTree(bt);

 

}

 

上述程序的執行果如圖1.20 所示

 

//銷毀樹bt

 

 

1.20

 

程序執行結果20

 

 

 

 

1

 

概論

 

 

1.33 分治法

 

1.3.1練習題

 

1.分治法的計思想是將一個難以直接解決的問題分割成規模小的子問題,分別解決子問題,后將子問題的解合起來形成原的解。這要求原題和子問題)。

 

A.問題規模相,問題性質相同

 

B.問題規模相同問題性質不同

 

C.問題規模不同問題性質相同

 

D.問題規模不,問題性質不同

 

2.在尋找n 個元中第k 小元素問中,如快速排序法思想,運用分算法對n個元素進行划分如何選擇划分基?下面()答解釋最合理。

 

A.隨機選擇一元素作為划分基准

 

B.取子序列的第個元素作為划分

 

C.用中位數的中數方法尋找划分

 

D.以上皆可行但不同方法,算法復雜度上界可不同

 

3.對於列二分查找算法以下正確的是()。

 

A.

 

intbinarySearch(inta[],intn,int x)

 

{int low=0, high=n-1;

 

while(low<=high)

 

{int mid=(low+high)/2;

 

if(x==a[mid]) returnmid;

 

if(x>a[mid]) low=mid;

 

else high=mid;

 

}

 

return 1;

 

}

 

B.

 

intbinarySearch(inta[],intn,int x)

 

{int low=0, high=n-1;

 

while(low+1!=high)

 

{int mid=(low+high)/2;

 

if(x>=a[mid]) low=mid;

 

else high=mid;

 

}

 

if(x==a[low]) returnlow;

 

else return 1;

 

}

 

C.

 

intbinarySearch(inta[],intn,intx)

 

{int low=0, high=n-1;

 

while(low<high-1)

 

{int mid=(low+high)/2;

 

21

 

 

 

 

算法設計

 

if(x<a[mid])

 

high=mid;

 

else low=mid;

 

}

 

if(x==a[low]) returnlow;

 

else return 1;

 

}

 

D.

 

intbinarySearch(inta[],intn,int x)

 

{if(n > 0 &&x >= a[0])

 

{int low = 0,high = n-1;

 

while(low < high)

 

{int mid=(low+high+1)/2;

 

if(x < a[mid])

 

high=mid-1;

 

else low=mid;

 

}

 

if(x==a[low]) returnlow;

 

}

 

return 1;

 

}

 

4.快速序算法是根據分策略來設計的,述其基本思想。

 

5.假設含n 個元素的待排的數據a 恰好是減排列的,說明調QuickSort(a0n-1)遞增排時間復雜度為O(n)

 

6.以下些算法采用分治略:

 

1)堆排序算

 

2)二路歸並序算法

 

3)折半查找

 

4)順序查找

 

7.適合行計算的問題通表現出哪些特征

 

8.設有兩個復數x=a+biy=c+di。復數乘積xy 可以使4 次乘法來完成xy=(ac-bd)+(ad+bc)i計一個僅用3 法來計算乘積xy 的方法。

 

9.4 數組abc d,都已經排好序說明找出這4 組的交集的方法。10.一個算法,采用分治法求一個數序列中的最大最小元素。

 

11.一個算法,采用分治法求x

 

12.假設二叉采用二叉鏈存儲結構進行存儲。計一個算法采用治法求一棵二叉bt 的高度。

 

13.假設二叉采用二叉鏈存儲結構進行存儲。計一個算法采用治法求一棵二叉bt 中度為2 點個數。

 

14.有一種二排序樹,其定義是空樹是一棵二排序樹,若不空左子樹中所有結點值小於根結點,右子樹中所有點值大於根結點,並且左右子樹是二叉排序樹。現在該二叉排序采用二叉鏈存儲采用分治法設計找值為x 的結點址,並分析算法的最好的平均時復雜度。

 

22

 

 

 

 

1

 

概論

 

 

15.設有n 不相同的整數,遞增順序存放在數組a[0..n-1],若存在一個下標i0i<n),使得a[i]=i。設計一個算法以O(log2n)時間找到這個i

 

16.模仿二分查找過程設計一個三查找算法。分析其時間復雜度。

 

17.對於1 的正整數n可以分解為n=x1*x2**xm,其中xi2例如,n=12 8 種不同的分式:12=1212=6*212=4*312=3*412=3*2*212=2*612=2*3*212=2*2*3,設計一個算法求n 不同分解式個數

 

18.設計一個基BSP 模型的並算法,假設有p 處理器,計算整數組a[0..n-1]的所有元素之和並分析算法的時復雜度。

 

1.3.2練習題參考答

 

1.答:C

 

2.答:D

 

3.答:a[]={12345}為例說明。選項A中在查找5 現死循環。選項B 中在查找5 時返-1。選項C 中在5 時返回-1D 正確。

 

4.答:無序序列a[low..high]行快速排序,整排序為大問題。選擇其中一個基准base=a[i](通常以序列中第一個元素為基),將所有小等於base 的元素移到它的前面,所大於等於base 素移動到它的后,即將基准歸位a[i],這樣產a[low..i-1]a[i+1..high]個無序序列,它的排序為小問。當a[low..high]列只有一個元素或者空時對應遞歸出

 

所以快速排序算就是采用分治策,將一個大問分解為兩個問題來求解。由於元素都a 數組中,其並過程是自然產的,不需要特別計。

 

5.答:此時快排序對應的遞歸高度為O(n)一次划分對應的時間為O(n)以整個排序時間O(n)

 

6.答:中二路歸並排序折半查找算法采分治策略。

 

7.答:並行計算的問通常表現出以下征:

 

1)將工作分成離散部分,有於同時解決。例,對於分治法設的串行算法,可以將各個立的子問題並行解,最后合並成個問題的解,從轉化為並行算法。

 

2)隨時並及地執行多個程序令。

 

3)多計算資下解決問題的耗要少於單個計算源下的耗時。

 

8.答:xy=(ac-bd)+((a+b)(c+d)-ac-bd)i由此可見,這樣xy只需要3次乘法(即acbd (a+b)(c+d)乘法運算)。

 

9.采用基本的二路並思路,先求ab 的交集ab求出cd 的交集cd最后求出ab cd的交集,即為最后的結果。也以直接采用4 並方法求解。

 

10.解:采用似求求一個整數序列中的最大次元素的分治法思。對應的程序如下:

 

#include <stdio.h>

 

#define max(x,y)((x)>(y)?(x):(y))

 

#define min(x,y)((x)<(y)?(x):(y))

 

23

 

 

 

 

算法設計

 

voidMaxMin(inta[],intlow,inthigh,int&maxe,int&mine)

 

//a最小

 

 

{if (low==high)

 

{maxe=a[low];

 

mine=a[low];

 

}

 

else if (low==high-1)

 

{maxe=max(a[low],a[high]);

 

mine=min(a[low],a[high]);

 

}

 

else

 

{int mid=(low+high)/2;

 

//只有一個元素

 

//只有兩個元素

 

//有兩個以上元素

 

 

int lmaxe,lmine;

 

MaxMin(a,low,mid,lmaxe,lmine);

 

int rmaxe,rmine;

 

MaxMin(a,mid+1,high,rmaxe,rmine);

 

maxe=max(lmaxe,rmaxe);

 

mine=min(lmine,rmine);

 

}

 

}

 

voidmain()

 

{int a[]={4,3,1,2,5};

 

int n=sizeof(a)/sizeof(a[0]);

 

int maxe,mine;

 

MaxMin(a,0,n-1,maxe,mine);

 

printf("Max=%d, Min=%d\n",maxe,mine);

 

}

 

上述程序的執行果如圖1.21 所示

 

 

1.21

 

程序執行結果

 

 

11.f(xn)=x,采用分治法解對應的遞歸模如下:

 

 

f(xn)=x

 

f(xn)=f(xn/2)*f(xn/2)

 

f(xn)=f(x(n-1)/2)*f(x(n-1)/2)*x對應的遞歸程序下:#include <stdio.h>

 

doublesolve(doublex,intn)

 

{double fv;

 

if (n==1) returnx;

 

if (n%2==0)

 

{fv=solve(x,n/2);

 

return fv*fv;

 

}

 

n=1 n 為偶數時n 為奇數時

 

//x^n

 

24

 

 

 

 

1

 

else

 

{fv=solve(x,(n-1)/2);

 

return fv*fv*x;

 

}

 

}

 

voidmain()

 

{double x=2.0;

 

printf("求解結果:\n");

 

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

 

printf("%g^%d=%g\n",x,i,solve(x,i));

 

}

 

上述程序的執行果如圖1.22 所示

 

概論

 

 

1.22

 

程序執行結果

 

 

12.f(bt)返回二叉樹bt 的高度,對應遞歸模型如下:

 

 

f(bt)=0

 

f(bt)=MAX{f(bt->lchild)f(bt->rchild)}+1對應的程序如下

 

#include "Btree.cpp"

 

intHeight(BTNode*bt)

 

{if (bt==NULL)return 0;

 

int lh=Height(bt->lchild);

 

int rh=Height(bt->rchild);

 

if (lh>rh) returnlh+1;

 

else return rh+1;

 

}

 

voidmain()

 

{BTNode *bt;

 

Int a[]={5,2,3,4,1,6};

 

Int b[]={2,3,5,1,4,6};

 

int n=sizeof(a)/sizeof(a[0]);

 

bt=CreateBTree(a,b,n);

 

bt=NULL

 

其他情況

 

//包含二叉樹的基運算算法//叉樹bt

 

//子問題1

 

//子問題2

 

//合並

 

//ab構造二叉bt

 

 

printf("二叉樹bt:");DispBTree(bt);printf("\n"); printf("bt的高度:%d\n",Height(bt));

 

 

DestroyBTree(bt);

 

//銷毀樹bt

 

 

}

 

 

25

 

 

 

 

算法設計

 

 

上述程序的執行果如圖1.23 所示

 

 

1.23

 

程序執行結果

 

 

13.f(bt)返回二叉樹bt 中度為2 的結個數,對應的遞模型如下:

 

 

f(bt)=0

 

f(bt)=f(bt->lchild)+f(bt->rchild)+1f(bt)=f(bt->lchild)+f(bt->rchild)

 

對應的算法如下

 

#include "Btree.cpp"

 

intNodes(BTNode*bt)

 

{int n=0;

 

bt=NULL

 

btNULL bt 分支結點其他情況

 

//包含二叉樹的基運算算法//bt2

 

 

if (bt==NULL) return0;

 

if (bt->lchild!=NULL&& bt->rchild!=NULL)

 

n=1;

 

return Nodes(bt->lchild)+Nodes(bt->rchild)+n;

 

}

 

voidmain()

 

{BTNode *bt;

 

Int a[]={5,2,3,4,1,6};

 

Int b[]={2,3,5,1,4,6};

 

int n=sizeof(a)/sizeof(a[0]);

 

 

bt=CreateBTree(a,b,n);

 

//ab構造二叉bt

 

 

printf("二叉樹bt:");DispBTree(bt);printf("\n"); printf("bt中度為2的結點個數:%d\n",Nodes(bt));

 

 

DestroyBTree(bt);

 

//銷毀樹bt

 

 

}

 

上述程序的執行果如圖1.24 所示

 

 

1.24

 

程序執行結果

 

 

14.解:f(btx)返回在二叉排序樹bt 的值為x 結點的址,若沒有找到返空,對應的遞歸型如下:

 

 

f(btx)=NULL

 

f(btx)=bt

 

f(btx)=f(bt->lchildx)

 

bt=NULL

 

btNULL x=bt->datax>bt->data

 

26

 

 

 

 

1

 

概論

 

 

f(btx)=f(bt->rchildx) 對應的程序如下

 

#include "Btree.cpp"

 

x<bt->data

 

//包含二叉樹的基運算算法

 

 

BTNode*Search(BTNode*bt,Int x)//二叉排序樹bt的值為x結點{if (bt==NULL)return NULL;

 

if (x==bt->data)return bt;

 

if (x<bt->data) returnSearch(bt->lchild,x);

 

else return Search(bt->rchild,x);

 

}

 

voidmain()

 

{BTNode *bt;

 

Int a[]={4,3,2,8,6,7,9};

 

Int b[]={2,3,4,6,7,8,9};

 

int n=sizeof(a)/sizeof(a[0]);

 

 

bt=CreateBTree(a,b,n);

 

//構造一棵二叉排bt

 

 

printf("二叉排序bt:");DispBTree(bt); printf("\n");int x=6;

 

BTNode *p=Search(bt,x);

 

if (p!=NULL)

 

printf("找到結點:%d\n",p->data);

 

else

 

printf("沒有找到\n",x);

 

 

DestroyBTree(bt);

 

//銷毀樹bt

 

 

}

 

上述程序的執行果如圖1.25 所示

 

 

1.25

 

程序執行結果

 

 

Search(btx)算法采的是減治法,最的情況是某個結左右子樹高度大致相同,其平均執行時間T(n)如下:

 

 

T(n)=1

 

T(n)=T(n/2)+1

 

n=1

 

n>1

 

 

可以推出T(n)=O(log2n),其中n 為二排序樹的結點個

 

15.解:二分查找方法。a[i]=i 時表示該元在有序非重復序a 中恰好第i 對於序列a[low..high]mid=(low+high)/2a[mid]=mid 表示找該元素;若a[mid]>mid說明右區間的所元素都大於其位,只能在左區間查找;若a[mid]<mid說明左區間的所有元素都小其位置,只能在間中查找。對的程序如下:

 

#include <stdio.h>

 

 

intSearch(inta[],intn)

 

{int low=0,high=n-1,mid;

 

//使得a[i]=i

 

27

 

 

 

 

算法設計

 

while (low<=high)

 

{mid=(low+high)/2;

 

if (a[mid]==mid)//查找到樣的元素

 

return mid;

 

else if (a[mid]<mid)//這樣的素只能在右區間中出現 low=mid+1;

 

 

else

 

//這樣的元素只能左區間中出現

 

 

high=mid-1;

 

}

 

return -1;

 

}

 

voidmain()

 

{int a[]={-2,-1,2,4,6,8,9};

 

int n=sizeof(a)/sizeof(a[0]);

 

int i=Search(a,n);

 

printf("求解結果\n");

 

if (i!=-1)

 

printf("存在a[%d]=%d\n",i,i);else

 

printf("不存在\n");

 

}

 

上述程序的執行果如圖1.26 所示

 

 

1.26

 

程序執行結果

 

 

16.解:有序序列a[low..high]若元素個數少於3個,直接查找若含有更多的元素,將其分為a[low..mid1-1]a[mid1+1..mid2-1]a[mid2+1..high]子序列,對每子序列遞歸查找,算的時間復雜度為O(log3n),屬於O(log2n)級別。對應的算法如下:

 

#include <stdio.h>

 

 

intSearch(inta[],intlow,inthigh,intx) {if (high<low)

 

return -1;

 

else if (high==low)

 

{if (x==a[low])

 

return low;

 

else

 

return -1;

 

}

 

if (high-low<2)

 

{if (x==a[low])

 

return low;

 

else if (x==a[low+1])

 

return low+1;

 

else

 

//查找

 

//序列中沒有元素

 

//序列中只有1

 

//序列中只有2

 

 

28

 

 

 

 

1

 

概論

 

 

return -1;

 

}

 

int length=(high-low+1)/3;

 

int mid1=low+length;

 

int mid2=high-length;

 

if (x==a[mid1])

 

return mid1;

 

else if (x<a[mid1])

 

return Search(a,low,mid1-1,x);

 

else if (x==a[mid2])

 

return mid2;

 

else if (x<a[mid2])

 

return Search(a,mid1+1,mid2-1,x);

 

else

 

return Search(a,mid2+1,high,x);

 

}

 

voidmain()

 

{int a[]={1,3,5,7,9,11,13,15};

 

int n=sizeof(a)/sizeof(a[0]);printf("求解結果\n");

 

int x=13;

 

int i=Search(a,0,n-1,x);

 

if (i!=-1)

 

printf("a[%d]=%d\n",i,x);

 

else

 

printf("不存在%d\n",x);

 

int y=10;

 

int j=Search(a,0,n-1,y);

 

if (j!=-1)

 

printf("a[%d]=%d\n",j,y);

 

else

 

printf("不存在%d\n",y);

 

}

 

上述程序的執行果如圖1.27 所示

 

//每個子序列的長

 

 

1.27

 

程序執行結果

 

 

17.f(n)表示n的不同分解式個數有:f(1)=1,作為遞歸

 

f(2)=1,分解式為2=2

 

f(3)=1,分解式為3=3

 

f(4)=2,分解式為4=44=2*2

 

29

 

 

 

 

算法設

 

f(6)=3,分解式為6=66=2*36=3*2,即f(6)=f(1)+f(2)+f(3)

 

以此類推,可以f(n)n 的所有數的不同分解式數之和,即f(n)= 𝑓(𝑛/𝑖)。對應的程序如

 

#include <stdio.h>

 

#define MAX 101

 

 

intsolve(int n)

 

{if (n==1) return1; else

 

{int sum=0;

 

//n分解式個數

 

 

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

 

if (n%i==0)

 

sum+=solve(n/i);

 

return sum;

 

}

 

}

 

voidmain()

 

{int n=12;

 

int ans=solve(n);

 

printf("結果: %d\n",ans);

 

}

 

上述程序的執行果如圖1.28 所示

 

1.28

 

程序執行結果

 

 

18.對應的並行算法下:

 

intSum(inta[],ints,intt,intp,int i)//理器i執行{int j,s=0;

 

for (j=s;j<=t;j++)

 

s+=a[j];

 

return s;

 

}

 

intParaSum(inta[],ints,intt,intp,int i)

 

{int sum=0,j,k=0,sj;

 

 

for (j=0;j<p;j++)

 

{sj=Sum(a,k,k+n/p-1,p,j);

 

k+=n/p;

 

}

 

sum+=sj;

 

return sum;

 

//for循環的各個問題並行執行

 

 

}

 

每個處理器的執時間為O(n/p),同開銷為O(p),所該算法的時間復度為O(n/p+p)

 

30

 

 

 

 

1

 

概論

 

 

1.44 蠻力法

 

1.4.1練習題

 

1.簡要較蠻力法和分治

 

2.在采蠻力法求解時什情況下使用遞歸

 

3.考慮下面個算法,它求的是數組a 中大小差最小的兩個元的差。請對這個算法做盡可能多改進。

 

#define INF 99999

 

 

#define abs(x) (x)<0?-(x):(x)

 

intMindif(inta[],intn)

 

{int dmin=INF;

 

for (int i=0;i<=n-2;i++)

 

//求絕對值宏

 

 

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

 

{int temp=abs(a[i]-a[j]);

 

if (temp<dmin)

 

dmin=temp;

 

}

 

return dmin;

 

}

 

4.給定一個整數A=(a0a1an-1),若i<jai>aj,則<aiaj>就為個逆序對。例如數組31452逆序對有<31><32><42><52>。設計一個算法采用蠻力A 中逆序對的數即逆序數。

 

5.於給定的正整數nn>1,采用蠻力1!+2!++n!改進該算法提高率。

 

6.有一群雞一群兔,它們的只數相同,它們腳數都是三位數且這兩個三位數的各位數字只能012345。設計一個算法用蠻力法求雞兔的只數各是多少?它們的腳數是多少?

 

7.有一個三數,個位數字比百位數字大,而位數字又比十位字大,並且各位數字之和等於各數字相乘之積,計一個算法用窮法求此三位數。

 

8.某年級的同集體去公園划船如果每只船坐10 ,那么多出2 位;如果每只船多坐2 人,么可少租1 只船設計一個算法用力法求該年級的多人數?

 

9.已知:若個合數的質因數分解式逐位相加和等於其本身逐相加之和,則稱這個數為Smith 。如4937775=3*5*5*65837,而3+5+5+6+5+8+3+7=424+9+3+7+7+7+5=42,所以4937775 Smith 求給定一個正整N,求大於N Smith 數。

 

輸入:若干個case每個case 一行代正整數N,輸入0 表示結束

 

輸出:大於N 最小Smith

 

輸入樣例:

 

4937774

 

0

 

樣例輸出:

 

31

 

 

 

 

算法設計

 

4937775

 

10.求解塗盤問題。小易有一塊n*n 的棋盤棋盤的每一個格都為黑色或者白色,小易現在要他喜歡的紅色去畫棋盤。小易會出棋盤中某一列擁有相同顏色的最大的區域去塗,幫助小易算算會塗畫多少個棋

 

輸入描述:輸入據包括n+1 行:一行為一個整數n1n50),即棋盤的小,接下來的n 每行一個字符串示第i 行棋盤的色,'W'表示白色,'B'表示黑色

 

輸出描述:輸出易會塗畫的區域小。

 

輸入例子:

 

3

 

BWW

 

BBB

 

BWB

 

輸出例子:

 

3

 

11.一個含nn>1整數元素的a,所元素不相同,采蠻力法求出a 有元素的全排列

 

1.4.2練習題參考答

 

1.答:蠻力是一種簡單直接地解決問題的方,適用范圍廣,能解決幾乎所有問題的一般性方,常用於一些非基本、但又十分要的算法(排序查找、矩陣乘法和字符串匹配等),蠻力法主解決一些規模小或值低的問題,可作為同樣問題的高效算法的一個准。而分治法采分而治之思路,一個復雜的問題成兩個或更多的相同或相似的子題,再把子問題成更小的子問題到問題解決。分法在求解問題時,通常性能比力法好。

 

2.答:如果蠻力法求解的問題可以分解為若個規模較小的相子問題,此時可以采用遞歸來實算法。

 

3.解:上述算的時間復雜度為O(n),采的是最基本的蠻力法。可以先a中元素遞增排序,然依次比較相鄰元的差,求出最小,改進后的算法下:

 

#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

intMindif1(inta[],intn)

 

 

{sort(a,a+n);

 

int dmin=a[1]-a[0];

 

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

 

{int temp=a[i]-a[i-1];

 

if (temp<dmin)

 

dmin=temp;

 

}

 

return dmin;

 

}

 

//遞增排序

 

32

 

 

 

 

1

 

概論

 

 

上述算法的主要間花費在排序上算法的時間復雜O(nlog2n)

 

4.解:用兩循環直接判斷是否為逆序對,法的時間復雜度為O(n2),比第3實驗3 算法的性差。對應的算法下:

 

 

intsolve(inta[],intn)

 

{int ans=0;

 

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

 

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

 

if (a[i]>a[j])

 

ans++;

 

return ans;

 

//序數

 

 

}

 

5.解:接采用蠻力法求算法如下:

 

 

longf(intn)

 

{long fn=1;

 

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

 

fn=fn*i;

 

return fn;

 

}

 

longsolve(intn)

 

{long ans=0;

 

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

 

ans+=f(i);

 

return ans;

 

//n!

 

//1!+2!+…+n!

 

 

}

 

實際上,f(n)=f(n-1)*nf(1)=1,在求f(n)時可以利用f(n-1)的結果。改后的算法如

 

 

下:

 

 

longsolve1(intn)

 

{long ans=0;

 

long fn=1;

 

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

 

{fn=fn*i;

 

ans+=fn;

 

}

 

return ans;

 

//1!+2!+…+n!

 

 

}

 

6.解:腳數為y=abc,兔數為z=def,有1ad50bcef5,采6 重循環,求雞只數x1=y/2y 2的倍數),兔只數x2=z/4z 4 的倍數),當x1=x2 時輸出結。對應的程序如

 

#include <stdio.h>

 

voidsolve()

 

{int a,b,c,d,e,f;

 

int x1,x2,y,z;

 

for (a=1;a<=5;a++)

 

for (b=0;b<=5;b++)

 

for (c=0;c<=5;c++)

 

33

 

 

 

 

算法設計

 

for (d=1;d<=5;d++)

 

for (e=0;e<=5;e++)

 

for (f=0;f<=5;f++)

 

{y=a*100+b*10+c;

 

z=d*100+e*10+f;

 

if (y%2!=0 || z%4!=0)

 

continue;

 

x1=y/2;

 

x2=z/4;

 

if (x1==x2)

 

//雞腳數

 

//兔腳數

 

//雞只數

 

//兔只數

 

 

printf("雞只數:%d,兔只數:%d,雞腳數:%d, 兔腳數:%d\n",x1,x2,y,z);

 

}

 

}

 

voidmain()

 

{printf("結果\n");

 

solve();

 

}

 

上述程序的執行果如圖1.29 所示

 

 

1.29程序執行

 

7.設該三數為x=abc,有1a90bc9滿足c>aa>ba+b+c=a*b*c。對的程序如下:

 

#include <stdio.h>

 

voidsolve()

 

{int a,b,c;

 

for (a=1;a<=9;a++)

 

for (b=0;b<=9;b++)

 

for (c=0;c<=9;c++)

 

{if (c>a && a>b&&a+b+c==a*b*c)

 

printf("%d%d%d\n",a,b,c);

 

}

 

}

 

voidmain()

 

34

 

 

 

 

1

 

概論

 

 

{

 

printf("求解結果\n");solve();

 

 

}

 

上述程序的執行果如圖1.30 所示

 

 

1.30程序執行

 

8.解:該年級的人數為x,租船數為y。因為每只船坐10 正好多出2 個座,則x=10*y-2;因為每船多坐2 人即12 時可少租1 只船沒有說恰好全部位占滿),有x+z=12*(y-1)z 示此時空出的座位,顯然z<12y 1 100實際上y取更大范圍的結果是相同的)、z011 ,求出最大的x 。對應的程序如

 

#include <stdio.h>

 

intsolve()

 

{int x,y,z;

 

for (y=1;y<=100;y++)

 

for (z=0;z<12;z++)

 

if (10*y-2==12*(y-1)-z)

 

x=10*y-2;

 

return x;

 

}

 

voidmain()

 

{printf("結果\n");

 

printf("最多人:%d\n",solve());

 

}

 

上述程序的執行果如圖1.31 所示

 

 

1.31程序執行

 

9.解:用蠻力法求出一正整數n的各位數字和sum1n的所有質因數的數字和sum2,若sum1=sum2,即為Smitch數。從用戶輸入的n開始枚舉,若是Smitch 數,輸出,本次束,否則n++繼續找大於n 的最小Smitch數。對應的整程序如下:

 

#include <stdio.h>

 

 

intSum(intn)

 

{int sum=0;

 

while (n>0)

 

//n數字

 

 

35

 

 

 

 

算法設計

 

{sum+=n%10;

 

n=n/10;

 

}

 

return sum;

 

}

 

boolsolve(intn)//n是否Smitch

 

{int m=2;

 

int sum1=Sum(n);

 

int sum2=0;

 

while (n>=m)

 

{if (n%m==0)//找到一個質因m

 

{n=n/m;

 

sum2+=Sum(m);

 

}

 

else

 

m++;

 

}

 

if (sum1==sum2)

 

return true;

 

else

 

return false;

 

}

 

voidmain()

 

{int n;

 

while (true)

 

{scanf("%d",&n);

 

if (n==0) break;

 

while (!solve(n))

 

n++;

 

printf("%d\n",n);

 

}

 

}

 

10.解:采用蠻力,統計每一列相鄰相同顏色的棋個數countj,在countj中求最大值。對應的程如下:

 

#include <stdio.h>

 

#define MAXN 51

 

//問題表示

 

int n;

 

char board[MAXN][MAXN];

 

 

intgetMaxArea()

 

{int maxArea=0;

 

for (int j=0; j<n;j++){int countj=1;

 

//法求解算法

 

 

for (int i=1; i<n;i++)//j列中相同顏色相鄰棋格個數 {if (board[i][j]==board[i-1][j])

 

countj++;

 

else

 

countj=1;

 

}

 

36

 

 

 

 

1

 

概論

 

 

if (countj>maxArea)

 

maxArea=countj;

 

}

 

return maxArea;

 

}

 

intmain()

 

{scanf("%d",&n);

 

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

 

scanf("%s",board[i]);

 

printf("%d\n",getMaxArea());

 

return 0;

 

}

 

11.解:與《程》中求全排列類似,但需要將1n 的全排列改按下標0n-1 a 的全排列(標從0 開始)。采用非遞的程序如下:

 

#include <stdio.h>

 

#include <vector>

 

using namespace std;

 

 

vector<vector<int>> ps;

 

//存放全排列

 

 

voidInsert(vector<int>s,inta[],inti,vector<vector<int>> &ps1)//個集合元素中間插入i得到ps1

 

{vector<int>s1;

 

vector<int>::iteratorit;

 

 

for (int j=0;j<=i;j++)

 

{s1=s;

 

it=s1.begin()+j;

 

s1.insert(it,a[i]);

 

ps1.push_back(s1);

 

}

 

}

 

voidPerm(inta[],intn)

 

{vector<vector<int>> ps1;

 

vector<vector<int>>::iterator it;

 

vector<int> s,s1;

 

s.push_back(a[0]);

 

ps.push_back(s);

 

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

 

{ps1.clear();

 

for (it=ps.begin();it!=ps.end();++it)

 

Insert(*it,a,i,ps1);

 

ps=ps1;

 

}

 

}

 

voiddispps()

 

//s(i個整數)每個位置插入a[i] //求出插入位置

 

//插入整數a[i]

 

//添加到ps1

 

//a[0..n-1]全排

 

//臨時存放子排列

 

//全排列迭代器

 

//添加{a[0]}集合

 

//循環添加a[1]a[n-1]

 

//ps1存放插入a[i]的結果

 

//在每個集合元素間插入a[i]得到ps1 //全排列ps

 

 

{vector<vector<int>>::reverse_iteratorit;//全排列的反向代器

 

 

vector<int>::iteratorsit;

 

//排列集合元素迭

 

 

for (it=ps.rbegin();it!=ps.rend();++it)

 

{for (sit=(*it).begin();sit!=(*it).end();++sit)

 

printf("%d",*sit);

 

printf("");

 

37

 

 

 

 

 

}

 

printf("\n");

 

}

 

voidmain()

 

{int a[]={2,5,8};

 

int n=sizeof(a)/sizeof(a[0]);printf("a[0%d]全排序如下:\nPerm(a,n);

 

dispps();


算法設計

 

",n-1);

 

 

}

 

上述程序的執行果如圖1.32 所示

 

 

1.32程序執行

 

1.55 回溯法

 

1.5.1練習題

 

1.回溯在問題的解空間中,按()策略從根結點出發搜解空間樹。

 

A.廣度優先B.活結點優先C.擴展點優先D.深度優

 

2.關於溯法以下敘述中正確的是()。

 

A.回溯法有用解題法之稱,它可以系統地索一個問題的所解或任意解

 

B.回溯法是一種帶系統性又帶有躍性的搜索算法

 

C.回溯算法需要助隊列這種結構保存從根結點到前擴展結點的路

 

D.回溯算法在成解空間的任一結點時,先判斷結點是否可能包問題的解,如果肯定不包含,則過對該結點為根子樹的搜索,逐向祖先結點回溯

 

3.回溯的效率不依賴於列哪些因素()。

 

 

A.確定解空間時間C.計算約束函數時間

 

B.滿足顯約束的的個數D.計算限界函的時間

 

 

4.下面)函數是回溯中為避免無效搜采取的策略。

 

A.遞歸函數B.剪枝函數C.隨機函數D.搜索函數

 

5.回溯法的搜索點是什么?

 

6.溯法解0/1 背包問時,該問題的解間是何種結構?回溯法解流水作調度問題時,該問的解空間是何種構?

 

7.對於遞增a[]={12345},采用5.4的回溯法求全排列,以12開頭的排列一定最先現嗎?為什么?

 

8.n 皇后問題,其空間樹為由12n 構成的n!排列所組成。現用回

 

38

 

 

 

 

1

 

概論

 

 

溯法求解,要求

 

1)通過解搜空間說明n=3 無解的。

 

2)給出剪枝

 

3)最壞情況在解空間樹上會成多少個結點?析算法的時間復度。9.設計一算法求解簡單裝載問題,設有一集裝箱要裝上一載重量為W

 

船,其中編號為i0in-1)的集箱的重量為wi。現要從n 個集裝中選出若干裝上輪船,使它們的量之和正好為W如果找到任一種返回true,否則返false

 

 

10.給定若干個正整a0a0 要求找選擇元素數最少的解。

 

an-1

 

,從中選出若干,使它們的和恰k

 

 

11.設計求解有復元素的排列問題的算法,設有n個元素a[]={a0a1an-1)其中可能含有重的元素,求這些素的所有不同排。如a[]={112},輸出結果是112),(121),(211)。

 

12.采用歸回溯法設計一個算法求1nn個整數中取出m個元素的排列要求每個元素最多只能取次。例如,n=3m=2的輸出結果是(12),(13),(2123),(31),(32

 

13.對於n問題,有人認為當n為偶數時,解具有對稱性,即n皇后問題的數恰好為n/2皇后題的解個數的2,這個結論正確嗎?請編寫回法程序對n=46810的情況進驗證。

 

14.給定一個向圖,由指定的起點前往指定的點,途中經過所其他頂點且只經過一次,稱為哈頓路徑,閉合的密頓路徑稱作哈頓回路(Hamiltoniancycle)。設計一個回溯算法求向圖的所有哈密回路。

 

1.5.2練習題參考答

 

1.答:D

 

2.答:回溯法是采用深度優先遍歷的,需要助系統棧結構來存從根結點到當前擴展結點的路。答案為C

 

3.答:溯法解空間是虛的,不必確定整解空間。答案為A

 

4.答:B

 

5.答:回溯在解空間樹中采用深度優先遍歷式進行解搜索,用約束條件和限界函數考察解向元素x[i]的取值,x[i]是合理的就搜索x[i]為根結點子樹,如果x[i]取完了所有的,便回溯到x[i-1]

 

6.用回溯法解0/1 問題時,該問題解空間是子集樹構。用回溯法解作業調度問題時該問題的解空間排列樹結構。

 

7.是的。對應的解空間是一棵排樹,如圖1.33所示給出前面3 部分,顯然最先產生的排列G 結點擴展出的葉子結點,它就是以12 的排列。

 

 

39

 

 

 

 

算法設計

 

A

 

 

1

 

2

 

3

 

4

 

5

 

 

B

 

C

 

D

 

E

 

F

 

 

2

 

3

 

4

 

5

 

 

G

 

H

 

I

 

1.33

 

J

 

部分解空間樹

 

 

8.1n=3 時的搜索空間如圖1.34所示,不能得任何葉子結點,有無解。

 

2)剪枝操作任何兩個皇后不同行、同列和同條對角線。

 

3)最壞情況每個結點擴展n 結點,共有n個結點,法的時間復雜度O(n)

 

(*,*,*)

 

 

(1,*,*)

 

(1,3,*)

 

(2,*,*)

 

(3,*,*)

 

(3,1,*)

 

 

1.34

 

3 皇后問題的解搜空間

 

 

9.用數組w[0..n-1]n 個集裝箱的量,采用類似判子集和是否存在方法求解。對應整的求解程序如

 

#include <stdio.h>

 

 

#define MAXN 20

 

//問題表示

 

int n=5,W;

 

int w[]={2,9,5,6,3};

 

int count;

 

voiddfs(inttw,intrw,inti) {if (i>=n)

 

{if (tw==W)

 

count++;

 

}

 

else

 

//最多集裝箱個數

 

//全局變量,累計個數

 

//簡單裝載問題

 

//找到一個葉子結

 

//找到一個滿足條的解,輸出它//尚未找完

 

 

{

 

}

 

rw-=w[i];

 

if (tw+w[i]<=W)

 

dfs(tw+w[i],rw,i+1);

 

if (tw+rw>=W)

 

dfs(tw,rw,i+1);

 

//求剩余的集裝箱量和

 

//左孩子結點剪枝選取滿足條件的集裝箱w[i] //選取第i個集裝

 

//右孩子結點剪枝剪除不可能存在解的結點 //不選取第i個集,回溯

 

 

}

 

boolsolve()

 

//簡單裝載問題是否存在解40

 

 

 

 

1

 

概論

 

 

{count=0;

 

int rw=0;

 

for (int j=0;j<n;j++)

 

rw+=w[j];

 

dfs(0,rw,0);

 

if (count>0)

 

return true;

 

else

 

return false;

 

}

 

voidmain()

 

{printf("結果\n");

 

//求所有集裝箱重rw//i0開始

 

 

W=4;

 

printf("W=%d%s\n",W,(solve()?"存在解":"沒有解"));W=10;

 

printf("W=%d%s\n",W,(solve()?"存在解":"沒有解"));W=12;

 

printf("W=%d%s\n",W,(solve()?"存在解":"沒有解"));W=21;

 

printf("W=%d%s\n",W,(solve()?"存在解":"沒有解"));}

 

本程序執行結果1.35 所示。

 

 

1.35

 

程序執行結果

 

 

10.解:這是個典型的解空間為子集樹的問題采用子集樹的回算法框架。當找到一個解后通過取的元素個數進比較求最優解minpath對應的完整程序下:

 

#include <stdio.h>

 

#include <vector>

 

using namespace std;

 

//問題表示

 

 

int a[]={1,2,3,4,5};

 

int n=5,k=9;

 

vector<int> minpath;

 

//求解結果表示

 

int minn=n;

 

voiddisppath()

 

{printf("擇的元素:");

 

//設置為全局變量 //存放最優解

 

//最多選擇n個元//一個解

 

 

for (int j=0;j<minpath.size();j++)

 

printf("%d ",minpath[j]);printf("元素個數=%d\n",minn);

 

 

}

 

 

41

 

 

 

 

算法設計

 

voiddfs(vector<int>path,intsum,intstart)//解算

 

 

{if (sum==k)

 

{if (path.size()<minn)

 

{minn=path.size();

 

minpath=path;

 

}

 

return;

 

}

 

if (start>=n) return;

 

dfs(path,sum,start+1);

 

//如果找到一個解不一定到葉子結點 //全部元素找完,

 

//不選擇a[start]

 

 

path.push_back(a[start]);//選擇a[start] dfs(path,sum+a[start],start+1);

 

}

 

voidmain()

 

 

{vector<int>path;

 

dfs(path,0,0); printf("最優解:\n");disppath();

 

//path存放一個子

 

 

}

 

上述程序的執行果如圖1.36 所示

 

 

1.36

 

程序執行結果

 

 

11.解:回溯法求全排列基礎上,增加元的重復性判斷。如,對於a[]={112},不判斷復性時輸出(112),(121),(112),(121211),211),共6個,有3 個是重的。重復性判斷這樣的,對於在a[i]時,僅僅a[i..j-1]沒有出現的元素a[j]交換a[i]的位置,如果現,對應的排列已經在前面求了。對應的完整序如下:

 

#include <stdio.h>

 

boolok(inta[],inti,intj)//ok

 

{if (j>i)

 

{for(int k=i;k<j;k++)

 

if (a[k]==a[j])

 

return false;

 

}

 

return true;

 

}

 

 

voidswap(int&x,int&y)

 

{int tmp=x;

 

x=y; y=tmp;

 

//兩個元素

 

 

}

 

voiddfs(inta[],intn,inti)//元素的排列問題{if (i==n)

 

42

 

 

 

 

1

 

概論

 

 

{for(int j=0;j<n;j++)

 

printf("%3d",a[j]);

 

printf("\n");

 

}

 

else

 

{for (int j=i;j<n;j++)

 

if (ok(a,i,j))//選取與a[i..j-1]重復的元素a[j] {swap(a[i],a[j]);

 

dfs(a,n,i+1);

 

swap(a[i],a[j]);

 

}

 

}

 

}

 

voidmain()

 

{int a[]={1,2,1,2};

 

int n=sizeof(a)/sizeof(a[0]);

 

printf("序列(");

 

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

 

printf("%d ",a[i]);

 

printf("%d)的所不同排列:\n",a[n-1]);

 

dfs(a,n,0);

 

}

 

上述程序的執行果如圖1.37 所示

 

 

1.37

 

程序執行結果

 

 

12.解:用求全排列的遞框架。選取的元個數用i 表示(i 1 開始),當i>m時達到一個葉子點,輸出一個排。為了避免重復used 數組實used[i]=0表示沒有選擇整數iused[i]=1 表示已經擇整數i。對應的完整程序如下:

 

#include <stdio.h>

 

#include <string.h>

 

#define MAXN 20

 

#define MAXM 10

 

int m,n;

 

 

int x[MAXM];

 

bool used[MAXN];

 

voiddfs(inti)

 

{if (i>m)

 

{for (int j=1;j<=m;j++)

 

printf("%d",x[j]);

 

printf("\n");

 

//x[1..m]存放一排列

 

//nm素的全排列//輸出一個排列

 

43

 

 

 

 

算法設計

 

 

}

 

else

 

{for (int j=1;j<=n;j++)

 

{if (!used[j])

 

{used[j]=true;

 

x[i]=j;

 

dfs(i+1);

 

used[j]=false;

 

}

 

}

 

}

 

}

 

voidmain()

 

{n=4,m=2;

 

memset(used,0,sizeof(used));printf("n=%d,m=%d求解結果\n",n,m); dfs(1);

 

}

 

上述程序的執行果如圖1.38 所示

 

//修改used[i]

 

//x[i]選擇j

 

//繼續搜索排列的一個元素//回溯:恢復used[i]

 

//初始化為0

 

//i1開始

 

 

1.38

 

程序執行結果

 

 

13.這個結論不正確。驗證程序如#include <stdio.h>

 

#include <stdlib.h>

 

#define MAXN 10

 

int q[MAXN];

 

 

bool place(int i)

 

{int j=1;

 

if (i==1) returntrue;

 

while (j<i)

 

//測試第i行的q[i]列上能否擺放皇后 //j=1i-1是已放了皇后的行

 

 

{if ((q[j]==q[i])|| (abs(q[j]-q[i])==abs(j-i)))

 

//該皇后是否與以皇后同列,位置(j,q[j])(i,q[i])否同對角線 return false;

 

j++;

 

}

 

return true;

 

 

}

 

 

44

 

 

 

 

1

 

概論

 

 

int Queens(int n)

 

{int count=0,k;

 

int i=1;

 

q[1]=0;

 

while (i>0)

 

{q[i]++;

 

//n皇后問題的個數//計數器初始化

 

//i為當前行

 

//q[i]為皇后i//移到下一列

 

 

while (q[i]<=n &&!place(i))

 

q[i]++;

 

if (q[i]<=n)

 

{if (i==n)

 

count++;//到一個解計數器count1 else

 

{

 

i++;; q[i]=0;

 

}

 

}

 

 

else i--;

 

}

 

return count;

 

}

 

void main()

 

{printf("結果如下:\n");for (int n=4;n<=10;n+=2)

 

//回溯

 

 

if (Queens(n)==2*Queens(n/2))

 

printf("n=%d: \n",n);

 

else

 

printf("n=%d: \n",n);

 

}

 

上述程序的執行果如圖1.39所示從執行結果看出結論是不正確的

 

 

1.39

 

程序執行結果

 

 

14.解:假設定的無向圖有n個頂點(頂點編0 n-1),采用鄰接陣數組a0/1 矩陣)存,求從頂點v出發回到頂點v 密頓回路。采用回溯法,解向x[0..n]x[i]表示第i步找到的頂點編號(i=n-1 時表示了起點v 外其他頂都查找了),初始時將起點v 放到x[0]i 1 始查找,i>0時循環:為x[i]找到一合適的頂點,i=n-1 時,若x[i]到頂點v有邊對應一個解;則繼續查找下一個頂點。如果x[i]找到一個合的頂點,則回溯采用非遞歸回溯架(與《教程》中求解n 皇后題的非遞歸回溯架類似)的完整序如下:

 

#include <stdio.h>

 

#define MAXV 10

 

45

 

 

 

 

 

//求解問題表示 int n=5;


算法設計

 

//圖中頂點個數

 

 

int a[MAXV][MAXV]={{0,1,1,1,0},{1,0,0,1,1},{1,0,0,0,1},{1,1,0,0,1},{0,1,1,1,0}};

 

//鄰接矩陣數組

 

//求解結果表示

 

int x[MAXV];

 

int count;

 

 

voiddispasolution()

 

{for (int i=0;i<=n-1;i++)

 

//一個解路徑

 

 

printf("(%d,%d) ",x[i],x[i+1]);

 

printf("\n");

 

 

}

 

boolvalid(inti)

 

//頂點第i個頂x[i]

 

 

{if (a[x[i-1]][x[i]]!=1)//x[i-1]x[i]沒有邊,返回false

 

return false;

 

for (int j=0;j<=i-1;j++)

 

if (x[i]==x[j])//頂點i出現,返回falsereturn false;

 

return true;

 

}

 

 

voidHamiltonian(intv)

 

{x[0]=v;

 

int i=1;

 

x[i]=-1;

 

while (i>0)

 

{x[i]++;

 

//頂點v出發哈密//存放起點

 

//從頂點-1+1=0試探

 

//尚未回溯到頭,

 

 

while (!valid(i)&& x[i]<n)

 

 

x[i]++;

 

if (x[i]<n)

 

//試探一個頂點x[i]

 

//找到一個有效的x[i]

 

 

{if (i==n-1)//達到葉結點

 

{if (a[x[i]][v]==1)

 

{x[n]=v;//到一個解

 

printf("%d:",count++);dispasolution();

 

}

 

}

 

else

 

{

 

i++; x[i]=-1;

 

}

 

}

 

else

 

 

i--;

 

//回溯

 

 

}

 

}

 

void main()

 

{printf("結果\n");

 

for (int v=0;v<n;v++)

 

{printf("頂點%d出發的哈密頓回路:\n",v); count=1;

 

46

 

 

 

 

1

 

概論

 

 

Hamiltonian(v);

 

//從頂點v出發

 

 

}

 

}

 

上述程序對如圖1.40所示的無向圖求從每個頂點出發哈密頓回路,程序執行結果如圖1.41 所示

 

1

 

 

0

 

3

 

2

 

4

 

 

1.40一個無向

 

 

1.41

 

程序執行結果

 

 

1.66 分枝限界法

 

1.6.1練習題

 

1.分枝界法在問題的解間樹中,按(略,從根結點出搜索解空間樹。A.廣度優先B.活結點優先C.擴展點優先D.深度優先

 

2.常見兩種分枝限界法)。

 

A.廣度優先限界法與深度優先分枝限界法

 

47

 

 

 

 

算法設計

 

B.隊列式(FIFO分枝限界法與堆式分枝限界法

 

C.排列樹法與子樹法

 

D.隊列式(FIFO)分枝限界法優先隊列式分枝限界法

 

3.分枝界法求解0/1 問題時,活結點的組織形式是()。

 

 

A.小根堆B.大根堆C.

 

4.采用大效益優先搜索式的算法是()。A.分支界限法B.動態規划C.貪心

 

D.數組

 

D.回溯法

 

 

5.優先列式分枝限界法取擴展結點的原是()。

 

A.先進先出B.后進先出C.結點優先級D.隨機

 

6.簡述枝限界法的搜索略。

 

7.有一個0/1 背包題,其中n=4品重量為(4753),物品價值40422512),背包最大重量W=10,給出采用先隊列式分枝限法求最優解的過

 

8.有一個流水作業調度問題,n=4a[]={51097}b[]={7598},給出用優先隊列式分限界法求一個解過程。

 

9.有一個n個頂點(頂點編號為0n-1的帶權圖,采用鄰接矩陣數組A表示,采用分枝限界法從起點s 到目標t 的最短路徑長,以及具有最短路徑長度的路徑數。

 

10.采用優先列式分枝限界法求解最優裝載問。給出以下裝載題的求解過程和結果:n=5,集箱重量為w=52643),限重為W=10。在裝載重量同時,最優裝載方案是集箱個數最少的方

 

1.6.2練習題參考答

 

1.答:A

 

2.答:D

 

3.答:B

 

4.答:A

 

5.答:C

 

6.答:分枝界法的搜索策略是廣度優先遍歷通過限界函數可快速找到一個解或者最優解。

 

7.答:解過程如下:

 

1)根結點1 隊,對應結點值e.i=0e.w=0e.v=0e.ub=76x:[0000]2)出隊結點1:左孩子結點2 進隊,對應結點e.no=2e.i=1e.w=4

 

e.v=40e.ub=76x:[1000];右孩子結點3進隊,對應結點值:e.no=3e.i=1e.w=0e.v=0e.ub=57x:[0000]

 

3)出隊結點2:左孩子超重;右孩子結點4 ,對應結點值:e.no=4e.i=2e.w=4e.v=40e.ub=69x:[1000]

 

4)出隊結點4:左孩子結點5 進隊,對應結點e.no=5e.i=3e.w=9e.v=65e.ub=69x:[1010];右孩子結點6進隊,對應結點值:e.no=6e.i=3e.w=4e.v=40e.ub=52x:[1000]

 

48

 

 

 

 

1

 

概論

 

 

5)出隊結點5:產生一個解,maxv= 65bestx:[1010]

 

6)出隊結點3:左孩子結點8 進隊,對應結點e.no=8e.i=2e.w=7e.v=42e.ub=57x:[0100];右孩子結點9被剪枝。

 

7)出隊結點8:左孩子超重;右孩子結點10 剪枝。

 

8)出隊結點6:左孩子結點11 超重;右孩子12 被剪枝。

 

9)隊列空,法結束,產生的優解:maxv= 65bestx:[1010]

 

8.答:解過程如下:

 

1)根結點1 隊,對應結點值e.i=0e.f1=0e.f2=0e.lb=29x[0000]

 

2)出隊結點1:擴展結點如下:

 

進隊(j=1):結2e.i=1e.f1=5e.f2=12e.lb=27x[1000]

 

進隊(j=2):結3e.i=1e.f1=10e.f2=15e.lb=34x[2000]進隊(j=3):結4e.i=1e.f1=9e.f2=18e.lb=29x[3000]

 

進隊(j=4):結5e.i=1e.f1=7e.f2=15e.lb=28x[4000]

 

3)出隊結點2:擴展結點如下:

 

進隊(j=2):結6e.i=2e.f1=15e.f2=20e.lb=32x[1200]進隊(j=3):結7e.i=2e.f1=14e.f2=23e.lb=27x[1300]進隊(j=4):結8e.i=2e.f1=12e.f2=20e.lb=26x[1400]4)出隊結點8:擴展結點如下:

 

進隊(j=2):結9e.i=3e.f1=22e.f2=27e.lb=31x[1420]進隊(j=3):結10e.i=3e.f1=21e.f2=30e.lb=26x[1430]5)出隊結點10,擴展一個j=2的子結點,有e.i=4到達葉子結點,產生的一個解

 

e.f1=31e.f2=36e.lb=31x=[1432]

 

該解對應的調度案是:第1 步執作業1,第2 行作業4,第3步執行作業3,第4 步執行2,總時間=36

 

9.采用優先隊列式分枝限界法求,隊列中結點的類型如下:

 

struct NodeType

 

 

{int vno;

 

int length;

 

bool operator<(constNodeType &s)const {return length>s.length;}

 

//頂點的編號

 

//當前結點的路徑//重載<關系函數 //length越小越優

 

 

};

 

從頂點s 開始度優先搜索,找到目標點t 后比求最短路徑長度及其路徑條數應的完整程序如

 

#include <stdio.h>

 

#include <queue>

 

using namespacestd;

 

#define MAX 11

 

#define INF 0x3f3f3f3f

 

//問題表示

 

 

int A[MAX][MAX]={

 

//一個帶權有向圖

 

 

49

 

 

 

 

 

{014INFINF}

 

{INF0INF15}

 

{INFINF0INF1}{INFINF203}

 

{INFINFINFINFINF} };

 

int n=5;

 

//求解結果表示

 

int bestlen=INF;

 

int bestcount=0;

 

struct NodeType

 

{int vno;

 

int length;


算法設計

 

//最優路徑的路徑//最優路徑的條數

 

//頂點的編號

 

//當前結點的路徑

 

 

bool operator<(constNodeType &s)const//重載>系函數

 

 

{return length>s.length;}

 

};

 

voidsolve(intsintt)

 

{NodeType ee1;priority_queue<NodeType>qu;

 

e.vno=s;

 

e.length=0;

 

qu.push(e);

 

while (!qu.empty())

 

{e=qu.top();qu.pop();

 

if (e.vno==t)

 

{if (e.length<bestlen)

 

{bestcount=1;

 

bestlen=e.length;

 

}

 

else if (e.length==bestlen)

 

bestcount++;

 

}

 

else

 

{for (int j=0;j<n;j++)

 

//length越小越優

 

//短路徑問題

 

//定義2個結點

 

//定義一個優先隊qu//構造根結點

 

//根結點進隊

 

//隊不空循環

 

//出隊結點e作為前結點//e是一個葉子結

 

//比較找最優解

 

//保存最短路徑長

 

//e不是葉子結點

 

//檢查e的所有相頂點

 

 

if (A[e.vno][j]!=INF&& A[e.vno][j]!=0)//頂點e.vno到頂點j有邊{if (e.length+A[e.vno][j]<bestlen)//剪枝

 

{e1.vno=j;

 

e1.length=e.length+A[e.vno][j];

 

 

qu.push(e1);

 

}

 

}

 

}

 

}

 

}

 

voidmain()

 

{int s=0t=4;

 

solve(st);

 

if (bestcount==0)

 

printf("頂點%d%d沒有路徑\n"st); else

 

{printf("%d%d存在路徑\n"st);

 

50

 

//有效子結點e1

 

 

 

 

1

 

概論

 

 

printf("最短徑長度=%d,條數=%d\n" bestlenbestcount);//輸出:5 3

 

}

 

}

 

上述程序的執行果如圖1.39 所示

 

 

1.39

 

程序執行結果

 

 

10.解:優先隊列式分枝限界法求解。設優先隊列priority_queue<NodeType>並設計優先隊列關系比較函數Cmp,指定按結點的ub值進行比較,即ub 越大的結點越先隊。對應的完整序如下:

 

#include <stdio.h>

 

#include <queue>

 

using namespace std;

 

 

#define MAXN 21

 

//問題表示

 

int n=5;

 

int W=10;

 

int w[]={0,5,2,6,4,3};//求解結果表示

 

int bestw=0;

 

int bestx[MAXN];

 

int Count=1;

 

typedef struct

 

{int no;

 

int i;

 

int w;

 

int x[MAXN];

 

int ub;

 

} NodeType;

 

struct Cmp

 

//最多的集裝箱數

 

//集裝箱重量,下標0的元素//存放最大重量,局變量

 

//存放最優解,變量

 

//搜索空間中結點累計,全局變量//結點編號

 

//當前結點在解空中的層次

 

//當前結點的總重

 

//當前結點包含的向量

 

//上界

 

//隊列中關系比較

 

 

{bool operator()(constNodeType&s,const NodeType&t)

 

{return (s.ub<t.ub)|| (s.ub==t.ub&& s.x[0]>t.x[0]);//ub越大越優先,ub相同時x[0]越小越優先

 

}

 

};

 

 

voidbound(NodeType&e)

 

{int i=e.i+1;

 

int r=0;

 

while (i<=n)

 

{r+=w[i];

 

i++;

 

}

 

e.ub=e.w+r;

 

//分枝結點e的上

 

//r為剩余集裝箱重量51

 

 

 

 

 

}

 

voidLoading()

 

{NodeType e,e1,e2;


算法設計

 

//載問題的最優解//定義3個結點

 

 

priority_queue<NodeType,vector<NodeType>,Cmp> qu;//定義個優先隊列qu

 

 

e.no=Count++;

 

e.i=0;

 

e.w=0;

 

for (int j=0; j<=n;j++) e.x[j]=0;

 

bound(e);

 

qu.push(e);

 

while (!qu.empty())

 

{e=qu.top();qu.pop();

 

if (e.i==n)

 

//設置結點編號

 

//根結點置初值,層次計為0//初始化根結點的向量

 

//求根結點的上界

 

//根結點進隊

 

//隊不空循環

 

//出隊結點e作為前結點//e是一個葉子結

 

 

{if ((e.w>bestw)|| (e.w==bestw&& e.x[0]<bestx[0]))//比較找最優解

 

 

{bestw=e.w;

 

//更新bestw

 

 

for (int j=0;j<=e.i;j++)

 

bestx[j]=e.x[j];//復制解向量e.x->bestx

 

 

}

 

}

 

else

 

{if (e.w+w[e.i+1]<=W)

 

{e1.no=Count++;

 

e1.i=e.i+1;

 

//e不是葉子結 //檢查左孩子結點 //設置結點編號 //建立左孩子結點

 

 

e1.w=e.w+w[e1.i];

 

for (int j=0; j<=e.i;j++)

 

e1.x[j]=e.x[j];//復制解向量e.x->e1.x

 

 

e1.x[e1.i]=1;

 

e1.x[0]++;

 

bound(e1);

 

qu.push(e1);

 

}

 

e2.no=Count++;

 

e2.i=e.i+1;

 

//選擇集裝箱i

 

//裝入集裝箱數增1//求左孩子結點的//左孩子結點進隊

 

//設置結點編號

 

//建立右孩子結點

 

 

e2.w=e.w;

 

for (int j=0; j<=e.i;j++)//解向量e.x->e2.xe2.x[j]=e.x[j];

 

 

e2.x[e2.i]=0;

 

bound(e2);

 

if (e2.ub>bestw)

 

qu.push(e2);

 

}

 

}

 

}

 

voiddisparr(intx[],intlen)

 

{for (int i=1;i<=len;i++)

 

printf("%2d",x[i]);

 

}

 

voiddispLoading()

 

{printf("X=[");

 

//不選擇集裝箱i

 

//求右孩子結點的

 

//若右孩子結點可,則進隊,否則被剪枝//一個解向量

 

//最優解

 

52

 

 

 

 

1

 

概論

 

 

disparr(bestx,n);

 

printf("],裝入總值為%d\n",bestw); }

 

voidmain()

 

{Loading();

 

printf("求解結果:\n");dispLoading();

 

}

 

上述程序的執行果如圖1.40 所示

 

//輸出最優解

 

 

1.40

 

程序執行結果

 

 

1.77 貪心法

 

1.7.1練習題

 

1.下面貪心算法的基本素的是()。

 

A.重疊子問題B.構造最優解C.貪心擇性質D.定義最

 

2.下面題()不能使貪心法解決。

 

A.單源最短路問題B.n 皇后問C.最小花費成樹問題D.背包問題

 

3.采用貪算法的最優裝載問題的主要計算在於將集裝箱依重量從小到大排序,故算法的時復雜度為()。

 

 

A.O(n)

 

B.O(n)

 

C.O(n)

 

D.O(nlog2n)

 

 

4.關於0/1背包問題以下描正確的是()。

 

A.可以使用貪算法找到最優解

 

B.能找到多項式間的有效算法

 

C.使用教材介紹動態規划方法可解任意01 背包

 

D.對於同一背與相同的物品,做背包問題取得總價值一定大於於做0/1 背包問

 

 

 

 

5.一棵哈曼樹共有215個結點,對其進行夫曼編碼,共能到()個不同的

 

 

字。

 

 

A.107

 

B.108

 

C.214

 

D.215

 

 

6.求解夫曼編碼中如何現貪心思路?

 

7.舉反例證明0/1背包問題若使用的算是按照vi/wi 的非遞減序考慮選擇的物品,即只要正在考慮的物品裝得就裝入背包,則方法不一定能得最優解(此題說0/1 背包問題背包問題的不同)。

 

53

 

 

 

 

算法設計

 

8.求解硬幣題。有1分、2分、5 分、10 50 分和100 硬幣各若干枚,現在要用這些硬幣支付W 元,最少要多少枚硬幣。

 

9.求解正整的最大乘積分解問題。將正整數n分解為若干個不相同的自然數之和,使這些自然的乘積最大。

 

10.求解船問題。有n ,第i個人體重為wi0i<n)。每艘船的大載重量均C,且最多只乘兩個人。用最的船裝載所有人

 

11.求解會議排問題。有一組A 和一組會議BA[i]表示第i 個會的參加人數,B[j]表示第j 會議室最多可以容納的人數。當僅當A[i]B[j]時,j個會議室可以用於舉辦第i 會議。給定數組A和數組B,試問最多可以同時舉多少個會議。例如,A[]={123}B[]={324},結果為3;若A[]={3431}B[]={1226},結果為2.

 

12.假設要在夠多的會場里安排一批活動,n 活動編號為1n,每個活動有開始

 

 

時間bi

 

和結束時間ei1in)。設計一個效的貪心算法求最少的會場個數

 

 

13.給定一個m×n的數字矩陣,算從左到右走過該矩陣且經過的格中整數最小的路徑。一條路徑以從第1 列的任位置出發,到達n 列的任意位置每一步為從第i 列走到第i+1 鄰行(水平移動或沿45 度斜線),如圖1.41所示。第1 行和后一行看作是相鄰的即應當把這個矩看成是一個卷起的圓筒。

 

 

1.41每一步的

 

兩個略有不同的5×6的數字矩陣的最小徑如圖1.42 所示只有最下面一行數不同。右邊矩陣的徑利用了第一行最后一行相鄰的質。

 

輸入:包含多個陣,每個矩陣的一行為兩個數mn,分別表示陣的行數和列數,接下來的m×n 個整數按行優的順序排列,即前n 個數組成第行,接下的n 組成第2 行,依類推。相鄰整數用一個或多個空分隔。注意這些不一定是正數。輸入中可能有一或多個矩陣描述直到輸入結束。個矩陣的行數在110 之間,列數在1 100

 

輸出:對每個矩輸出兩行,第一為最小整數之和路徑,路徑由n 整數組成,表示路徑經過的號,如果這樣的徑不止一條,輸字典序最小一條

 

 

3

4

1

2

8

6

 

6

1

8

2

7

4

5

5

9

6

3

9

9

5

83

44 1

18 6

3

2

6

 

3

7

2

8

6

4

 

 

 

3

4

1

2

8

6路徑

 

6

1

8

2

7

4

5

9

3

9

9

5

8

4

1

3

2

6

3

7

2

1

2

3

 

 

 

54

 

 

 

 

1

 

概論

 

 

6 1 8 2 7 4

 

5 9 3 9 9 5

 

8 4 1 3 2 6

 

3 7 2 8 6 4

 

輸出結果:

 

1 2 3 4 4 5

 

16

 

1.7.2練習題參考答

 

1.答:C

 

2.答:n皇后問題的解滿足貪心選擇性質。答案為B

 

3.答:D

 

4.由於背包問題以取物品的一部,所以總價值一大於等於做0/1題。答案為D

 

5.這里n=215夫曼樹中n1=0,而n0=n2+1n=n0+n1+n2=2n0-1n0=(n+1)/2=108案為B

 

6.答:在構哈夫曼樹時每次都是將兩棵根結最小的樹合並,而體現貪心的思路。

 

7.證明:例如n=3w={322}v={744}W=4 時,由於7/3最大,若按題目要求的方法,能取第一個,收7。而此實例最大的收益應該是8,取第23個物品。

 

8.用結構體數組A 放硬幣數據A[i].v 存放硬幣i 的面A[i].c 存放硬幣i 枚數。采用貪心路,首先將數組A按面額遞減排序,再兌換硬幣每次盡可能兌換面額大的硬幣。對應完整程序如下:

 

#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

#define min(x,y)((x)<(y)?(x):(y))

 

#define MAX 21

 

//問題表示

 

int n=7;

 

struct NodeType

 

 

{int v;

 

int c;

 

//面額

 

//枚數

 

 

bool operator<(constNodeType &s)

 

 

{

 

//用於按面額遞減

 

 

return s.v<v;

 

}

 

};

 

NodeType A[]={{1,12},{2,8},{5,6},{50,10},{10,8},{200,1},{100,4}};

 

int W;

 

//求解結果表示

 

 

int ans=0;

 

voidsolve()

 

//兌換的硬幣枚數//硬幣

 

55

 

 

 

 

 

{sort(A,A+n);


算法設計

 

//按面額遞減排序

 

 

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

 

{int t=min(W/A[i].v,A[i].c);//使用硬幣i的枚數 if (t!=0)

 

printf("支付%3d面額:%3d\n",A[i].v,t);

 

 

W-=t*A[i].v;

 

ans+=t;

 

if (W==0) break;

 

}

 

}

 

voidmain()

 

{W=325;

 

//剩余的金額

 

//支付的金額

 

 

printf("支付%d:\n",W);

 

solve();

 

printf("最少硬幣個數:%d\n",ans);}

 

上述程序的執行果如圖1.43 所示

 

 

1.43

 

程序執行結果

 

 

9.解:用貪心方法求解a[0..k]存放n 解結果:

 

1n4 時可驗證其分解成幾正整數的和的乘均小於n,沒有解。

 

2n>4 時,n分拆成若干個互不相等的自數的和,分解數的個數越多乘積大。為此讓n 解數個數盡可能(體現貪心的思),把n分解成從2開始的連續的自然數之和。例,分解n a[0]=2a[1]=3a[2]=4a[k]=k+2(共有k+1 個分解),用m表示下數,這樣的分直到ma[k],即mk+2。對剩下數m 的處為如下兩種情況

 

m<k+2:將m平均分解到a[k..i]對應的分解數個m)中,即從a[k]開始往前的分解數增加1也是貪心的思路分解數越大加1 乘積也越大)。

 

m=k+2a[0..k-1](對應的分數個數為k)的每分解數增加1下的2 加到a[k]中,即a[k]增加2

 

對應的完整程序下:

 

#include <stdio.h>

 

#include <string.h>

 

#define MAX 20

 

//問題表示

 

int n;

 

//求解結果表示

 

56

 

 

 

 

1

 

概論

 

 

int a[MAX];

 

int k=0;

 

voidsolve()

 

{int i;

 

int sum=1;

 

if (n<4)

 

return;

 

else

 

{int m=n;

 

a[0]=2;

 

m-=a[0];

 

k=0;

 

while (m>a[k])

 

{k++;

 

//存放被分解的數

 

//a[0..k]存放被解的數

 

//n的最乘積

 

//不存在最優方案,直接返回

 

//m表示剩下數

 

//第一個數從2

 

//減去已經分解的

 

//若剩下數大於最一個分解數,則繼續分解 //a數組下標+1

 

 

a[k]=a[k-1]+1;//234順序分解

 

 

m-=a[k];

 

}

 

if (m<a[k])

 

//減去最新分解的

 

//若剩下數小於a[k],a[k]開始往前的數+1

 

 

{for (i=0; i<m;i++)

 

a[k-i]+=1;

 

}

 

 

if (m==a[k])

 

{a[k]+=2;

 

//若剩下數等於a[k],a[k]的值+2,之前的數+1

 

 

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

 

a[i]+=1;

 

}

 

}

 

}

 

voidmain()

 

{n=23;

 

memset(a,0,sizeof(a));

 

solve();

 

printf("%d的最優解方案\n",n);int mul=1;

 

printf("分解的:");

 

for (int i=0;i<=k;i++)

 

if (a[i]!=0)

 

{printf("%d ",a[i]);

 

mul*=a[i];

 

}

 

printf("\n乘積大值:%d\n",mul); }

 

上述程序的執行果如圖1.44 所示

 

 

57

 

 

 

 

 

1.44


算法設計

 

程序執行結果

 

 

10.解:采用心思路,首先按體重遞增排序;考慮前后的兩個(最輕者和最重),分別用ij 向:若w[i]+w[j]C,說明這兩個可以同乘(執行i++j--),否則w[j]單乘(執行j--),若最后只余一個人,該人只單乘。

 

對應的完整程序下:

 

#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

#define MAXN 101

 

//問題表示

 

int n=7;

 

int w[]={50,65,58,72,78,53,82};

 

int C=150;

 

//求解結果表示

 

int bests=0;

 

 

voidBoat()

 

{sort(w,w+n);

 

int i=0;

 

int j=n - 1;

 

while (i<=j)

 

{if(i==j)

 

//乘船問題//遞增排序

 

//剩下最后一個人

 

 

{printf("艘船:%d\n",w[i]); bests++;

 

break;

 

}

 

if (w[i]+w[j]<=C)//前后兩個人同

 

{printf("艘船:%d%d\n",w[i],w[j]); bests++;

 

i++;

 

j--;

 

}

 

 

else

 

//w[j]單乘

 

 

{printf("艘船:%d\n",w[j]); bests++;

 

j--;

 

}

 

}

 

}

 

voidmain()

 

{printf("結果:\n");

 

Boat();

 

printf("最少的船=%d\n",bests);

 

}

 

上述程序的執行果如圖1.45 所示

 

58

 

 

 

 

1

 

概論

 

 

1.45

 

程序執行結果

 

 

11.解:采用心思路。每次都在還未安排的容最大的會議室安盡可能多的參會人數,即對於每會議室,都安排前還未安排的會中,參會人數最的會議。若能容納下,則選擇該議,否則找參會數次多的會議來排,直到找到能納下的會議。

 

對應的完整程序下:

 

#include <stdio.h>

 

#include <algorithm>

 

using namespace std;

 

//問題表示

 

 

int n=4;

 

int m=4;

 

int A[]={3,4,3,1};

 

int B[]={1,2,2,6};//求解結果表示 int ans=0;

 

voidsolve()

 

{sort(A,A+n);

 

sort(B,B+m);

 

//會議個數 //會議室個數

 

//算法//遞增排序 //遞增排序

 

 

int i=n-1,j=m-1;//從最多數會議和最多容納人數會議室開始

 

for(i;i>=0;i--)

 

{if(A[i]<=B[j]&& j>=0)

 

{ans++;//不滿足條件,加一個會議室

 

j--;

 

}

 

}

 

}

 

voidmain()

 

{solve();

 

printf("%d\n",ans);//輸出2

 

}

 

12.與《教程》例7.2類似,會場對應欄,只是這里僅求會場個數,即兼容活動子集的數。對應的完整序如下:

 

#include <stdio.h>

 

#include <string.h>

 

#include <algorithm>

 

using namespace std;

 

#define MAX 51

 

//問題表示

 

 

struct Action

 

//活動的類型聲明59

 

 

 

 

 

{int b;

 

int e;


算法設計

 

//活動起始時間 //活動結束時間

 

 

bool operator<(constAction &s) const//重載<關系函數

 

 

{if (e==s.e)

 

return b<=s.b;

 

else

 

return e<=s.e;

 

}

 

//結束時間相同按始時間遞增排序//否則按結束時間增排序

 

 

};

 

int n=5;

 

Action A[]={{0},{1,10},{2,4},{3,6},{5,8},{4,7}};//下標0不用 //求解結果表示

 

 

int ans;

 

voidsolve()

 

{bool flag[MAX];

 

memset(flag,0,sizeof(flag));

 

sort(A+1,A+n+1);

 

ans=0;

 

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

 

{if (!flag[j])

 

{flag[j]=true;

 

int preend=j;

 

//最少會場個數

 

//最大兼容活動子集//活動標志

 

//A[1..n]按指定式排序//會場個數

 

//前一個兼容活動下標

 

 

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

 

{if (A[i].b>=A[preend].e&& !flag[i]){preend=i;

 

flag[i]=true;

 

}

 

}

 

 

ans++;

 

//增加一個最大兼活動子集

 

 

}

 

}

 

}

 

voidmain()

 

{solve();

 

printf("求解結果\n");

 

printf("最少場個數:%d\n",ans);//4

 

}

 

13.解:采用心思路。從第1 開始每次查找a[i][j]元素上、中、下3 應數中的最小數。對應程序如下

 

#include <stdio.h>

 

#define M 12

 

#define N 110

 

int m=5, n=6;

 

int a[M][N]={{3,4,1,2,8,6},{6,1,8,2,7,4},{5,9,3,9,9,5},{8,4,1,3,2,6},{3,7,2,8,6,4}};

 

int minRow,minCol;

 

intminValue(inti,intj)

 

//a[i][j]方上、中、3的最小數,同時把行

 

 

{

 

int s = (i == 0)? m - 1 : i - 1; int x = (i == m -1)?0:i+1;

 

 

60

 

 

 

 

1

 

概論

 

 

minRow = s;

 

minRow = a[i][j+1]< a[minRow][j+1]?i:minRow;

 

minRow = a[x][j+1]< a[minRow][j+1]?x:minRow;

 

minRow = a[minRow][j+1]== a[s][j+1]&& minRow > s?s:minRow;minRow = a[minRow][j+1]== a[i][j+1]&& minRow > i?i:minRow;minRow = a[minRow][j+1]== a[x][j+1]&& minRow > x?x:minRow;return a[minRow][j+1];

 

}

 

voidsolve()

 

{int i,j,min;

 

for (j=n-2; j>=0;j--)

 

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

 

a[i][j]+= minValue(i,j);

 

min=a[0][0];

 

minRow=0;

 

 

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

 

if (a[i][0]<min)

 

{min=a[i][0];

 

minRow=i;

 

}

 

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

 

{printf("%d",minRow+1);

 

if (j<n-1) printf("");

 

minValue(minRow,j);

 

}

 

printf("\n%d\n",min);

 

}

 

voidmain()

 

{

 

solve();

 

}

 

//在第一列查找最代價的行

 

 

1.88 動態規划

 

1.8.1練習

 

1.下列法中通常以自底上的方式求解最解的是()。

 

A.備忘錄法B.動態規划C.貪心法D.溯法2.備忘方法是()算的變形。

 

 

A.分治法

 

B.回溯法

 

C.貪心法D.規划法

 

 

3.下列動態規划算法基要素的是()。

 

A.定義最優解B.構造最優C.算出最優解D.問題重疊性質4.一個題可用動態規划法或貪心算法求的關鍵特征是問的()。

 

A.貪心選擇性B.重疊子問C.最優子結構性質D.定義最優5.簡述態規划法的基本路。

 

6.簡述態規划法與貪心的異同。

 

61

 

 

 

 

算法設計

 

7.簡述態規划法與分治的異同。

 

8.下列法中哪些屬於動規划算法?

 

1)順序查找

 

2)直接插入序算法

 

3)簡單選擇序算法

 

4)二路歸並序算法

 

9.某個題對應的遞歸模如下:

 

f(1)=1

 

f(2)=2

 

 

f(n)=f(n-1)+f(n-2)++f(1)+1

 

可以采用如下遞算法求解:longf(intn)

 

{if (n==1) return1;

 

if (n==2) return2;

 

long sum=1;

 

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

 

sum+=f(i);

 

return sum;

 

n>2

 

 

}

 

但其中存在大量重復計算,請采備忘錄方法求解

 

10.3 章中實驗4采用分治法求解半數集問,如果直接遞歸解會存在大量重復計算,請改進算法。

 

11.設計一個時復雜度為O(n)的算法計算二項式系數Cnkn)。二項式系Cn的求值過如下:

 

𝐶=1

 

𝐶=1

 

 

𝑗𝑗1𝑗

 

𝑖𝑖1𝑖1

 

ij

 

 

12.一個器人只能向下和向右移動,每次能移動一步,設一個算法求它從00)移動到mn)有多少條徑。

 

13.兩種水果交出一種新水果,現在給新水果名,要求這個名中包含了以前兩種水果名字的字,並且這個名字盡量短。也就是以前的一種水果arr1 是新水果名字arr 的子序,另一種水果名arr2 也是新水果arr 的子序列。計一個算法求arr

 

例如:輸入以下3組水果名稱:

 

apple peach

 

ananas banana

 

pear peach

 

輸出的新水果名如下:

 

62

 

 

 

 

1

 

概論

 

 

appleach

 

bananas

 

pearch

 

1.8.2練習題參考答

 

1.答:B

 

2.答:D

 

3.答:D

 

4.答:C

 

5.答:動態划法的基本思路是將待求解問題解成若干個子問,先求子問題的解,然后從這些問題的解得到原題的解。

 

6.答:規划法的3個基本要素是最優子構性質、無后效和重疊子問題性質,而貪心法的個基本要素是貪選擇性質和最優結構性質。所以者的共同點是都要求問題具有最子結構性質。

 

兩者的不同點如

 

1)求解方式同,動態規划法自底向上的,有具有最優子結構質的問題只能用動態規划法有些可用貪心法而貪心法是自頂下的。

 

2)對子問題依賴不同,動態划法依賴於各子題的解,所以應使各子問題最優,才能保證整最優;而貪心法賴於過去所作過選擇,但決不依於將來的選擇,也不依賴於子問的解。

 

7.答:兩者共同點是將待求解的問題分解成干子問題,先求子問題,然后再從這些子問題的得到原問題的解

 

兩者的不同點是適合於用動態規法求解的問題,解得到的各子問往往不是相互獨立的(重疊問題性質),而分治法子問題相互獨立;另外動規划法用表保存已解過的子問題解,再次碰到同的子問題時不必新求解,而只需詢答案,故可獲得多項式級時間雜度,效率較高而分治法中對於次出現的子問題求解,導致同樣的子問題被反復解,故產生指數長的時間復雜度效率較低。

 

8.答:判斷算是否具有最優子構性質、無后效和重疊子問題性質。(2)、(3和(4)均屬於態規划算法。

 

9.解:一個dp 數組,dp[i]對應f(i)的值,首先dp 的所有元初始化為0,在f(i)時,若dp[0]>0表示f(i)已經求,直接返回dp[i]可,這樣避免了復計算。對應的算法如下:

 

long dp[MAX];//dp[n]保存f(n)的計算結果

 

longf1(intn)

 

{if (n==1)

 

{dp[n]=1;

 

return dp[n];

 

}

 

if (n==2)

 

{dp[n]=2;

 

return dp[n];

 

63

 

 

 

 

算法設計

 

}

 

if (dp[n]>0) returndp[n];

 

long sum=1;

 

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

 

sum+=f1(i);

 

dp[n]=sum;

 

return dp[n];

 

}

 

10.設計一個數組a其中a[i]=f(i),首先a的所有元素初始化為0,當a[i]>0時表示對應的f(i)經求出,直接返就可以了。對應完整程序如下:

 

#include <stdio.h>

 

#include <string.h>

 

#define MAXN 201

 

//問題表示

 

int n;

 

int a[MAXN];

 

 

intfa(inti)

 

{int ans=1;

 

if (a[i]>0)

 

return a[i];

 

for(int j=1;j<=i/2;j++)

 

ans+=fa(j);

 

a[i]=ans;

 

return ans;

 

}

 

intsolve(int n)

 

{memset(a,0,sizeof(a));

 

a[1]=1;

 

return fa(n);

 

}

 

voidmain()

 

{n=6;

 

//a[i]

 

//set(n)

 

 

printf("求解結果\n");

 

printf("n=%d數集元素個數=%d\n",n,solve(n));

 

 

}

 

11.定義C(ij)=Ci

 

ij。則有如下推計算公式:C(inj)=C(i-1j-1)+C(i-

 

 

1j),初始條件C(i0)=1C(ii)=1可以根據初始由此遞推關系計C(nk)

n

Cn。對應的序如下:

 

#include <stdio.h>

 

#define MAXN 51

 

#define MAXK 31

 

//問題表示

 

int n,k;

 

//求解結果表示

 

int C[MAXN][MAXK];

 

voidsolve()

 

{int i,j;

 

64

 

 

 

 

1

 

概論

 

 

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

 

{C[i][i]=1;

 

C[i][0]=1;

 

}

 

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

 

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

 

C[i][j]=C[i-1][j-1]+C[i-1][j];

 

}

 

voidmain()

 

{n=5,k=3;

 

solve();

 

printf("%d\n",C[n][k]);//輸出10

 

}

 

顯然,solve()的時間復雜度為O(n)

 

12.設從(00)移動到(ij)的路條數為dp[i][j],由機器人只能向下向右移動,不同迷宮問題(迷宮題由於存在后退不滿足無后效性不適合用動態規划法求解)。對應的狀轉移方程如下:

 

dp[0][j]=1

 

dp[i][0]=1

 

 

dp[i][j]=dp[i][j-1]+dp[i-1][j]

 

ij>0

 

 

最后結果是dp[m][n]。對應的程如下:

 

#include <stdio.h>

 

#include <string.h>

 

#define MAXX 51

 

#define MAXY 51

 

//問題表示

 

int m,n;

 

//求解結果表示

 

int dp[MAXX][MAXY];

 

voidsolve()

 

{int i,j;

 

dp[0][0]=0;

 

memset(dp,0,sizeof(dp));

 

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

 

dp[i][0]=1;

 

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

 

dp[0][j]=1;

 

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

 

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

 

dp[i][j]=dp[i][j-1]+dp[i-1][j];

 

}

 

voidmain()

 

{m=5,n=3;

 

solve();

 

printf("%d\n",dp[m][n]);

 

}

 

13.解:本題目的路是求arr1 arr2字符串的最長公共子序,基本過程參見

 

65

 

 

 

 

 

程》第8 8.5 。對應的完整程如下:#include <iostream>

 

#include <string.h>

 

#include <vector>

 

#include <string>

 

using namespace std;

 

#define max(x,y)((x)>(y)?(x):(y))

 

#define MAX 51

 

//問題表示

 

int m,n;

 

string arr1,arr2;

 

//求解結果表示

 

int dp[MAX][MAX];

 

vector<char> subs;

 

voidLCSlength()

 

{int i,j;

 

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

 

dp[i][0]=0;

 

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

 

dp[0][j]=0;

 

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

 

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


算法設計

 

//序列中最多的字個數

 

//動態規划數組

 

//存放LCS

 

//dp

 

//dp[i][0]置為0,邊界條件

 

//dp[0][j]置為0,邊界條件

 

//兩重for循環處arr1arr2的所有字符

 

 

{if (arr1[i-1]==arr2[j-1])//比較的字符相同

 

dp[i][j]=dp[i-1][j-1]+1;

 

 

else

 

//比較的字符不同

 

dp[i][j]=max(dp[i][j-1],dp[i-1][j]);

 

 

}

 

}

 

voidBuildsubs()

 

{int k=dp[m][n];

 

int i=m;

 

int j=n;

 

while (k>0)

 

//dpsubs

 

//karr1arr2最長公共子序列長度

 

//subs中放入最公共子序列(反向)

 

 

if (dp[i][j]==dp[i-1][j])i--;

 

else if (dp[i][j]==dp[i][j-1])j--;

 

else

 

{subs.push_back(arr1[i-1]);//subs添加arr1[i-1] i--; j--; k--;

 

}

 

}

 

voidmain()

 

 

{cin >> arr1>> arr2; m=arr1.length();

 

n=arr2.length();

 

LCSlength();

 

Buildsubs();

 

cout << "求解結"<<endl;

 

cout << "arr:";vector<char>::reverse_iteratorrit;

 

//輸入arr1arr2 //marr1的長度 //narr2的長度 //求出dp

 

//求出LCS

 

66

 

 

 

 

1

 

for (rit=subs.rbegin();rit!=subs.rend();++rit)

 

cout << *rit;

 

cout << endl;

 

cout << ":"<<dp[m][n]<< endl;

 

概論

 

 

}

 

改為如下:

 

13.解:本題目思路是先求arr1 arr2 字符串的最公共子序列,基本過程參見《教程》第8 8.5節,再利用遞歸輸新水果取名。

 

算法中設置二維態規划數組dpdp[i][j]表示arr1[0..i-1]i 個字母)和arr2[0..j-1]j 個字母)中長公共子序列的長度。另外設置維數組bb[i][j]arr1 arr2 比較3 種情況:b[i][j]=0表示arr1[i-1]=arr2[j-1]b[i][j]=1表示arr1[i-1]arr2[j-1]並且dp[i- 1][j]>dp[i][j-1]b[i][j]=2表示arr1[i-1]arr2[j-1]並且dp[i-1][j]dp[i][j-1]

 

對應的完整程序下:

 

#include <stdio.h>

 

#include <string.h>

 

 

#define MAX 51

 

//問題表示

 

int m,n;

 

char arr1[MAX],arr2[MAX];

 

//求解結果表示

 

int dp[MAX][MAX];

 

int b[MAX][MAX];

 

void Output(int i,intj)

 

{if (i==0 &&j==0)

 

return;

 

if(i==0)

 

{Output(i,j-1);

 

printf("%c",arr2[j-1]);

 

return;

 

}

 

else if(j==0)

 

{Output(i-1,j);

 

printf("%c",arr1[i-1]);

 

return;

 

}

 

if (b[i][j]==0)

 

{Output(i-1,j-1);

 

printf("%c",arr1[i-1]);

 

return;

 

}

 

else if(b[i][j]==1)

 

{Output(i-1,j);

 

printf("%c",arr1[i-1]);

 

return;

 

}

 

else

 

{Output(i,j-1);

 

//序列中最多的字個數

 

//動態規划數組

 

//存放arr1arr2較的3種情況//利用遞歸輸出新果取名

 

//輸出完畢

 

//arr1完畢,輸出arr2的剩余部分

 

//arr2完畢,輸arr1的剩余部分

 

//arr1[i-1]=arr2[j-1]情況

 

 

67

 

 

 

 

算法設計

 

 

printf("%c",arr2[j-1]);

 

return;

 

}

 

}

 

void LCSlength()

 

{int i,j;

 

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

 

dp[i][0]=0;

 

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

 

dp[0][j]=0;

 

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

 

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

 

{if (arr1[i-1]==arr2[j-1])

 

{dp[i][j]=dp[i-1][j-1]+1;

 

b[i][j]=0;

 

}

 

else if (dp[i-1][j]>dp[i][j-1])

 

{dp[i][j]=dp[i-1][j];

 

b[i][j]=1;

 

}

 

else

 

{dp[i][j]=dp[i][j-1];

 

b[i][j]=2;

 

}

 

}

 

}

 

void main()

 

{int t;

 

printf("測試用例:");scanf("%d",&t);

 

while(t--)

 

{scanf("%s",arr1);

 

scanf("%s",arr2);

 

memset(b,-1,sizeof(b));

 

m=strlen(arr1);

 

n=strlen(arr2);

 

LCSlength();

 

printf("結果: ");Output(m,n); printf("\n");

 

}

 

}

 

上述程序的一次行結果如圖1.46 示。

 

//dp

 

//dp[i][0]置為0,邊界條件

 

//dp[0][j]置為0,邊界條件

 

//兩重for循環處arr1arr2的所有字符 //比較的字符相同情況0

 

//情況1

 

//dp[i-1][j]<=dp[i][j-1]:2

 

//輸入測試用例個

 

//marr1的長度

 

//narr2的長度

 

//求出dp

 

//輸出新水果取名

 

 

68

 

 

 

 

1

 

概論

 

 

1.46

 

程序的一次執行結

 

 

13.解:本題目的路是求arr1 arr2字符串的最長公共子序,基本過程參見程》第8 8.5 。對應的完整程如下:

 

然后再用遞歸思,逐一輸出,得的就是最后答案

 

#include <iostream>

 

#include <string.h>

 

#include <vector>

 

#include <string>

 

using namespace std;

 

#define max(x,y)((x)>(y)?(x):(y))

 

 

#define MAX 51

 

//問題表示

 

int m,n;

 

string arr1,arr2;

 

//求解結果表示

 

int dp[MAX][MAX];

 

vector<char> subs;

 

voidLCSlength()

 

{int i,j;

 

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

 

dp[i][0]=0;

 

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

 

dp[0][j]=0;

 

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

 

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

 

//序列中最多的字個數

 

//動態規划數組

 

//存放LCS

 

//dp

 

//dp[i][0]置為0,邊界條件

 

//dp[0][j]置為0,邊界條件

 

//兩重for循環處arr1arr2的所有字符

 

 

{if (arr1[i-1]==arr2[j-1])//比較的字符相同

 

dp[i][j]=dp[i-1][j-1]+1;

 

 

else

 

//比較的字符不同

 

dp[i][j]=max(dp[i][j-1],dp[i-1][j]);

 

 

}

 

}

 

voidBuildsubs()

 

{int k=dp[m][n];

 

int i=m;

 

int j=n;

 

while (k>0)

 

//dpsubs

 

//karr1arr2最長公共子序列長度

 

//subs中放入最公共子序列(反向)

 

 

if (dp[i][j]==dp[i-1][j])i--;

 

else if (dp[i][j]==dp[i][j-1])j--;

 

69

 

 

 

 

算法設

 

else

 

{subs.push_back(arr1[i-1]);//subs添加arr1[i-1] i--; j--; k--;

 

}

 

}

 

voidmain()

 

 

{cin >> arr1>> arr2; m=arr1.length();

 

n=arr2.length();

 

LCSlength();

 

Buildsubs();

 

//輸入arr1arr2 //marr1的長度 //narr2的長度 //求出dp

 

//求出LCS

 

 

cout << "求解結"<<endl;

 

cout << "arr:";

 

vector<char>::reverse_iteratorrit;

 

for (rit=subs.rbegin();rit!=subs.rend();++rit)

 

cout << *rit;

 

cout << endl;

 

cout << ":"<<dp[m][n]<< endl;

 

}

 

上述程序的一次行結果如圖1.46示。

 

 

1.46

 

程序的一次執行結

 

 

1.99 圖算法設計

 

1.9.1練習題

 

1.以下屬於貪心算法的)。

 

A.Prim 算法B.Kruskal C.Dijkstra算法D.深度優遍歷2.一個有n 頂點的連通圖的生成樹是原圖的小連通子圖,且含原圖中所有n

 

個頂點,並且有持圖聯通的最少邊。最大生成樹是權和最大生成,現在給出一個無向帶權圖的鄰矩陣為{{04503}{40423}{54020}{02201}{33010}},其中權為0 沒有邊。一個圖求這個圖的最大生成樹的權和是()。

 

 

A.11

 

B.12

 

C.13

 

D.14

 

E.15

 

 

3.某個帶權通圖有4個以上的頂點,其中恰2 條權值最小邊,盡管該圖的最小生成樹可能多個,而這2 值最小的邊一定含在所有的最小成樹中嗎?如果3 條權值最小邊呢?

 

70

 

 

 

 

1

 

概論

 

 

4.為什TSP 問題采用心算法求解不一定得到最優解

 

5.求最路徑的4 種算法合帶權無向圖嗎

 

6.單源最短路徑的算法有Dijkstra 法、Bellman-Ford 算法和SPFA 法,比較這些算法的不同點

 

7.有人這樣修Dijkstra 算法以便求一個帶權連通圖的單源最長路,將每次選擇dist 最小的頂點u改為選擇最大的頂點u,將按路長度小進行調整為按路徑長度大調整。這樣可以求源最長路徑嗎?

 

8.給出一種方法無環帶權連通圖所有權值非負)從頂點s 到頂點t的一條最長簡單路徑。

 

9.一個運輸網如圖1.47 所示,上數字為(c(ij)b(ij)),其中c(ij)表示容量,b(ij)表示單運輸費用。給出123 位置運貨物到位置6的最小費用最大的過程。

 

10.本教中的Dijkstra 算法用鄰接矩陣存儲,算法時間復雜O(n)。請你從各個方面考慮優該算法,用於求v 到其他頂點最短路徑長度。

 

11.有一個帶權向圖G(所有權正整數),采用鄰接陣存儲。設計一個算法求其中的一個最小

 

 

1

 

(4,2)

 

 

(6,4)

 

4

 

(12,3)

 

 

2

 

(3,5)

 

6

 

 

(3,4)

 

(1,6)

 

5

 

(9,12)

 

 

3

 

(2,3)

 

 

1.47

 

一個運輸網絡

 

 

1.9.2練習題參考答

 

1.答:D

 

2.答:采用Kurskal 算法求最大生成樹,第1 步取最大邊02),第2步取邊(01),第3取邊(04),第4取最大邊(13),得到的權14。答案為D

 

3.答:2 權值最小的邊一包含在所有的最生成樹中,因為Kurskal算法一定首先選中這2 權值最小的邊。果有3 條權值最的邊,就不一定,因為首先選中3 條權值最小邊有可能出現回

 

4.答:TSP 問題滿足最優子結構性質,如(01230)是整個問題的最優解,但(0120)不一定是問題的最優解。

 

5.答:適合帶權無向圖最短路徑。

 

6.答:Dijkstra 不適合存在負權的圖求單源最短徑,其時間復雜O(n)Bellman-Ford 算法SPFA 算法適存在負權邊的圖單源最短路徑,圖中不能

 

71

 

 

 

 

算法設計

 

存在權值和為負環。Bellman-Ford 法的時間復雜度O(ne),而SPFA算法的時間復雜度為O(e),所SPFA 算法更

 

7.答:不能。Dijkstra算法本質上是一種貪心法,而求單源最長路徑不滿足心選擇性質。

 

8.答:Bellman-Ford 算法和SPFA算法適合存在負權邊的圖求源最短路徑。將圖中所有邊權改為負權值,求從頂點s 到頂點t的一條最短簡單路徑,它就是原圖中從頂點s 頂點t的一條最長簡單路徑。

 

9.答:為該運網絡添加一個虛起點0,它到123 位置運輸費0,容量分別為到123 置運輸容量和,1.48 所示,起s=0,終點t=6

 

 

1

 

(4,2)

 

 

(10,0)

 

(6,4)

 

4

 

(12,3)

 

 

0

 

(6,0)

 

2

 

(3,5)

 

6

 

 

(3,0)

 

(3,4)

 

(1,6)

 

5

 

(9,12)

 

 

3

 

(2,3)

 

 

1.48

 

添加一個虛擬起點運輸網絡

 

 

首先初始化f 流,最大流量maxf=0,最小費用mincost=0,采最小費用最大流算法求解過程如

 

1k=0,求出w如下:

 

 

0

0

0

0

0

2

4

0

5

4

0

6

3

0

3

0

12

0

 

 

求出從起點0 6 的最短路徑0→1→4→6,求最小調整量=4f[4][6]調整4f[1][4]調整4f[0][1]調整為4mincost=20maxf=4

 

2k=1,求出w如下:

 

 

0

0

0

0

0

0

4

0

5

4

0

6

3

-2

0

3

0

12

-3

0

 

 

求出從起點0 6 的最短路徑0→2→4→6,求最小調整量=3f[4][6]調整

 

72

 

 

 

 

1

 

概論

 

 

7f[2][4]調整3f[0][2]調整為3mincost=44maxf=4+3=73k=2,求出w如下:

 

 

0

0

0

0

0

0

4

0

0

4

0

6

3

-2

-5

0

3

0

12

-3

0

 

 

求出從起點0 6 的最短路徑0→3→4→6,求最小調整量=1f[4][6]調整8f[3][4]調整1f[0][3]調整為1mincost=53maxf=7+1=8

 

4k=3,求出w如下:

 

 

0

0

0

0

0

0

4

0

0

4

0

0

3

-2

-5

-6

0

3

0

12

-3

0

 

 

求出從起點0 6 的最短路徑0→3→5→6,求最小調整量=2f[5][6]調整2f[3][5]調整2f[0][3]調整為3mincost=83maxf=8+2=10

 

5k=4,求出w如下:

 

 

0

0

0

0

0

4

0

0

4

0

0

-2

-5

-6

0

3

-3

0

12

-3

-12

0

 

 

求出從起點0 6 的最短路徑0→1→5→6,求最小調整量=6f[5][6]調整8f[1][5]調整6f[0][1]調整為10mincost=179maxf=10+6=16

 

6k=5,求出w如下:

 

 

0

0

0

0

0

0

4

0

0

-2

-5

-6

0

3

-4

-3

0

12

-3

-12

0

 

 

求出從起點0 6 的最短路徑0→1→5→6,求最小調整量=1f[5][6]調整

 

73

 

 

 

 

算法設計

 

9f[2][5]調整1f[0][2]調整為4mincost=195maxf=16+1=177k=6,求出w中沒有增廣路徑,調整結束對應的最大流如下:

 

 

0

10

4

3

0

0

0

0

0

0

0

4

6

0

0

0

0

0

3

1

0

0

0

0

0

1

2

0

0

0

0

0

0

0

8

0

0

0

0

0

0

9

0

0

0

0

0

0

0

 

 

最終結果,maxf=17mincost=195。即輸的最大貨物量17,對應的最總運輸費用為195

 

10.從兩個方面考慮優化:

 

1)在Dijkstra 法中,當求出源v 到頂點u 的最路徑長度后,僅僅調整從頂u 出發的鄰接的最短路徑長度而教程中的Dijkstra算法由於采用鄰接矩陣存儲需要花費O(n)時間來調整頂點u 出發的鄰接的最短路徑長度,如果采用鄰接存儲圖,可以很快查到頂點u 的所有接點並進行調整時間為O(MAX(圖中頂點的出))

 

2)求目前一最短路徑長度的u 時,教科書Dijkstra 算法采簡單比較方法,可以改為用優先隊列(小堆)求解。由於e 條邊對應的點進隊,對應的時間為O(log2e)

 

對應的完整程序測試數據算法如

 

 

#include "Graph.cpp"

 

#include <queue>

 

#include <string.h>

 

using namespace std;

 

ALGraph *G;

 

struct Node

 

{int i;

 

int v;

 

//包含圖的基本運算法

 

//圖的鄰接表存儲構,作為全局變量 //聲明堆中結點類

 

//頂點編號

 

//dist[i]

 

 

friend bool operator<(constNode &aconst Node &b)//定義比較運算符 {returna.v >b.v;}

 

};

 

voidDijkstra(intvintdist[])//Dijkstra

 

{ArcNode *p;

 

 

priority_queue<Node>qu;

 

Node e;

 

int S[MAXV];

 

//創建小根堆

 

//S[i]=1表示頂點iS中,S[i]=0表示頂點iU

 

 

int ijuw;

 

memset(S0sizeof(S));

 

p=G->adjlist[v].firstarc;

 

for (i=0;i<G->n;i++)dist[i]=INF;

 

while (p!=NULL)

 

{w=p->adjvex;

 

 

74

 

 

 

 

1

 

概論

 

 

dist[w]=p->weight;

 

e.i=w; e.v=dist[w];

 

qu.push(e);

 

p=p->nextarc;

 

}

 

S[v]=1;

 

for (i=0;i<G->n-1;i++)

 

//距離初始化

 

//v的出邊頂點qu

 

//源點編號v放入S

 

//循環直到所有頂的最短路徑都求出

 

 

{e=qu.top();qu.pop();//出隊e

 

 

u=e.i;

 

S[u]=1;

 

p=G->adjlist[u].firstarc;

 

while (p!=NULL)

 

{w=p->adjvex;

 

if (S[w]==0)

 

//選取具有最小最路徑長度的頂點u

 

//頂點u加入S

 

//考察從頂點u的所有相鄰點

 

//考慮修改不在S的頂點w的最短路徑長度

 

 

if (dist[u]+p->weight<dist[w])

 

{dist[w]=dist[u]+p->weight;//修改最短路徑長度 e.i=w; e.v=dist[w];

 

qu.push(e);//改最短路徑長度的頂點進隊

 

}

 

p=p->nextarc;

 

}

 

}

 

}

 

voidDisppathlength(intvintdist[])//出最短

 

{printf("%d頂點出發的最短路徑長度如下:\n"v);

 

for (int i=0;i<G->n;++i)

 

if (i!=v)

 

printf("到頂點%d:%d\n"idist[i]);

 

}

 

voidmain()

 

{int A[MAXV][MAXV]={

 

{0466INFINFINF}

 

{INF01INF7INFINF}

 

{INFINF0INF64INF}

 

{INFINF20INF5INF}

 

{INFINFINFINF0INF6}

 

{INFINFINFINF108}

 

{INFINFINFINFINFINF0}};

 

int n=7 e=12;

 

 

CreateAdj(GAne);printf("G的鄰:\n");DispAdj(G);

 

int v=0;

 

int dist[MAXV];

 

Dijkstra(vdist);

 

Disppathlength(vdist);

 

DestroyAdj(G);

 

//建立圖的鄰接表 //輸出鄰接表

 

//調用Dijkstra//輸出結果

 

//銷毀圖的鄰接表

 

 

}

 

上述程序的執行果如圖1.49 所示

 

 

75

 

 

 

 

算法設計

 

 

1.49程序執行

 

其中Dijkstra 的時間復雜度為O(n(log2e+MAX(點的出度)),一圖中最大頂點出度遠小於e以進一步簡化時間復雜度為O(nlog2e)

 

11.有一個帶權向圖G(所有權正整數),采用鄰接陣存儲。設計一個算法求其中的一個最小

 

解:利用Floyd 法求出所有頂點之間的最短路徑,若頂點i j 最短路徑,而圖中又存在頂點ji的邊,則構成一個環,在所環中比較找到一最小環並輸出。應的程序如下:

 

 

#include "Graph.cpp"

 

//包含圖的基本運算法

 

 

#include <vector>

 

using namespace std;

 

voidDispapath(intpath[][MAXV],inti,intj) //頂點ij的一

 

 

{vector<int>apath;

 

int k=path[i][j];

 

apath.push_back(j);

 

while (k!=-1 && k!=i)

 

{apath.push_back(k);

 

k=path[i][k];

 

}

 

apath.push_back(i);

 

//存放一條最短路中間頂點(反向)//路徑上添加終點

 

//路徑上添加中間

 

//路徑上添加起點

 

 

for (int s=apath.size()-1;s>=0;s--)//輸出路徑上中間頂點printf("%d→",apath[s]);

 

}

 

intMincycle(MGraphg,intA[MAXV][MAXV],int&mini,int&minj)//gA中的查找

 

{int i,j,min=INF;

 

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

 

for (j=0;j<g.n;j++)

 

if (i!=j && g.edges[j][i]<INF)

 

{if (A[i][j]+g.edges[j][i]<min)

 

{min=A[i][j]+g.edges[j][i];

 

mini=i; minj=j;

 

76

 

 

 

 

1

 

概論

 

 

}

 

}

 

return min;

 

}

 

voidFloyd(MGraphg)

 

{int A[MAXV][MAXV],path[MAXV][MAXV];

 

int i,j,k,min,mini,minj;

 

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

 

for (j=0;j<g.n;j++)

 

{A[i][j]=g.edges[i][j];

 

//Floydg

 

 

if (i!=j && g.edges[i][j]<INF)

 

 

path[i][j]=i;

 

else

 

path[i][j]=-1;

 

}

 

for (k=0;k<g.n;k++)

 

{for (i=0;i<g.n;i++)

 

for (j=0;j<g.n;j++)

 

//頂點ij有邊時

 

//頂點ij沒有邊

 

//依次考察所有頂

 

 

if (A[i][j]>A[i][k]+A[k][j])

 

{A[i][j]=A[i][k]+A[k][j];//修改最短路徑長度 path[i][j]=path[k][j];//最短路徑

 

}

 

}

 

min=Mincycle(g,A,mini,minj);

 

if (min!=INF)

 

{printf("最小環:");

 

 

Dispapath(path,mini,minj);printf("%d, 長度%d\n",mini,min);

 

}

 

else printf("中沒有任何環\n");

 

}

 

voidmain()

 

{MGraph g;

 

//輸出一條最短路

 

 

int A[MAXV][MAXV]={{0,5,INF,INF},{INF,0,1,INF},

 

{3,INF,0,2}, {INF,4,INF,0}};

 

int n=4, e=5;

 

 

CreateMat(g,A,n,e);printf("G的鄰矩陣:\n");DispMat(g);

 

Floyd(g);

 

//建立圖的鄰接矩//輸出鄰接矩陣

 

 

}

 

上述程序的執行果如圖1.50 所示

 

 

77

 

 

 

 

算法設計

 

 

1.50程序執行

 

1.1010計算

 

1.10.1練習題

 

1.對如1.51 所示的點A,給出采用Graham 掃描算法求包的過程及結果

 

 

10

9

8

7

6

5

4

3

2

 

1

 

a8

 

a11

 

a9

 

a0

 

a6

 

a7

 

a5

 

a3

 

a10

 

a4

 

a1

 

a2

 

 

12345

 

678910

 

 

1.51一個點集A

 

2.對如1.51 所示的點A,給出采用分治法求最近點對過程及結果。

 

3.對如1.51 所示的點A,給出采用旋轉卡殼法求最遠對的結果。

 

4.對應3 個點向p1p2p3,采S(p1p2p3)=(p2-p1)(p3-p1)/2求它們構成的三角形面積,請問么情況下計算結為正?什么情況計算結果為負?

 

 

5.知坐標為整數,出判斷平面上一p 是否在一個逆針三角形p1-p2-p3的算法。

 

1.10.2練習題參考答

 

內部

 

 

1.答:Graham 掃描法求凸包的過程及結果如下:

 

求出起點a0(11)

 

排序后:a0(11)a1(81)a2(94)a3(54)a4(87)a5(56)a10(710)a9(35) a6(37) a7(410)a8(16)a11(03)

 

先將a0(11)a1(81)進棧,a2(94)進棧。

 

處理點a3(54)a3(54)進棧。

 

處理點a4(87)a3(54)存在右拐系,退棧,a4(87)進棧。

 

78

 

 

 

 

1

 

概論

 

 

處理點a5(56)a5(56)進棧。

 

處理點a10(710)a5(56)存在右拐關系,退a10(710)進棧。

 

處理點a9(35)a9(35)進棧。

 

處理點a6(37)a9(35)存在右拐系,退棧,a6(37)進棧。

 

處理點a7(410)a6(37)存在右拐關系,退棧a7(410)進棧。

 

處理點a8(16)a8(16)進棧。

 

處理點a11(03)a11(03)進棧。

 

結果:n=8,凸的頂點:a0(11)a1(81)a2(94)a4(87)a10(710)a7(410) a8(16) a11(03)

 

2.答:解過程如下:

 

排序前:(11)(81)(94)(54)(87)(56)(37)(410)(16)(35)(710)(03)。按x 排序后:(03)(11)(16)(37)(35)(410)(54)(56)(710) (81)(87)(94)。按y 坐標排序后(11)(81)(03)(54)(94)(35)(16) (56) (37) (87) (410) (710)

 

1)中間位置midindex=5左部分:(03)(11)(16)(37)(35)(410);右部分:(54)(56)(710)(81)(87)(94);中間部分點(03)(37)(410)(54) (56) (710) (87)

 

2)求解左部(03) (11) (16) (37) (35)(410)

 

中間位置=2,划為左部分1(03)(11)(16),右部分1(37) (35) (410)處理左部分1數少於4:求出近距離=2.23607(03)(11)之間的距離。處理右部分1數少於4:求出近距離=2,即(37)(35)之間的離。再考慮中間部分中間部分最近距=2.23)求出左部d1=2

 

3)求解右部(54) (56) (710) (81) (87)(94)

 

中間位置=8,划為左部分2(54)(56)(710),右部分2(81)(87)(94)

 

處理左部分2數少於4,求出近距離=2,即(54)(56)之間距離。處理右部分2數少於4,求出近距離=3.16228(81)(94)之間的距離。再考慮中間部分中間部分為空)出右部分d2=2

 

4)求解中間分點集:(03)(37)(410)(54)(56)(710)(87)。求出最近距離d3=5

 

最終結果為:d=MIN{d1d2d3)=2

 

3.答:采用旋轉殼法求出兩個最遠點對是(11)和(710),最遠距離10.82

 

 

4.當三角形p1-p2-p3

 

逆時針方向時,1.52 所示,p2-p1

 

p3-p1

 

的順時針方

 

 

向上,(p2-p1)(p3-p1)>0,對應的面(p2-p1)(p3-p1)/2為正。

 

 

當三角形p1-p2-p3

 

順時針方向時,1.53 所示,p2-p1

 

p3-p1

 

的逆時針方向上

 

 

(p2-p1)(p3-p1)<0,對應的面積(p2-p1)(p3-p1)/2為負。

 

79

 

 

 

 

算法設計

 

 

p1

 

p3-p1

 

p2-p1

 

p1

 

p2-p1

 

p3-p1

 

 

1.52

 

p1-p2-p3

 

逆時針方向圖

 

1.53p1-p2-p3

 

逆時針方向

 

 

5.答:S(p1p2p3)=(p2-p1)(p3-p1)/2求三角形p1p2p3 號的的面積。如圖1.54 所示,若S(pp2p3)S(pp3p1)S(pp1p2)3 個三角的方向均為逆時方向)均大於0表示p 在該三角內部。

 

p3

 

 

p1

 

p

 

p2

 

 

1.54

 

一個點p 和一個三

 

 

對應的程序如下

 

#include "Fundament.cpp"

 

//包含向量基本運算法

 

 

doublegetArea(Pointp1,Pointp2,Pointp3)//

 

{

 

return Det(p2-p1,p3-p1);

 

}

 

boolIntrig(Pointp,Pointp1,Pointp2,Pointp3)//pp1p2p3{double area1=getArea(p,p2,p3);

 

double area2=getArea(p,p3,p1);

 

double area3=getArea(p,p1,p2);

 

if (area1>0 && area2>0&& area3>0)

 

return true;

 

else

 

return false;

 

}

 

voidmain()

 

{printf("結果\n");

 

Point p1(0,0);

 

Point p2(5,-4);

 

Point p3(4,3);

 

Point p4(3,1);

 

Point p5(-1,1);

 

printf("p1:");p1.disp(); printf("\n");

 

printf("p2:");p2.disp(); printf("\n");

 

printf("p3:");p3.disp(); printf("\n");

 

printf("p4:");p4.disp(); printf("\n");

 

80

 

 

 

 

1

 

概論

 

 

printf("

 

printf("

 

printf("

 

printf("

 

p5:"); p5.disp();printf("\n");

 

p1p2p3三角形面積:%g\n",getArea(p1,p2,p3));

 

p4p1p2p3三角形:%s\n",Intrig(p4,p1,p2,p3)?"":"不是");p5p1p2p3三角形:%s\n",Intrig(p5,p1,p2,p3)?"":"不是");

 

 

}

 

上述程序的執行果如圖1.55 所示

 

 

1.55程序執行

 

1.1111計算雜性理論

 

1.11.1練習題

 

1.旅行問題是NP 嗎?

 

 

A.

 

B.

 

C.至今尚無定論

 

 

2.下面P 問題,NP問題和NPC 題,說法錯誤的是()。

 

A.如果一個問可以找到一個能在多項式的時間解決它的算法,么這個問題就屬P 問題

 

B.NP 問題是指以在多項式的時里驗證一個解的問題

 

C.所有的P 題都是NP問題

 

D.NPC 問題不定是個NP 問題只要保證所有的NP問題都可以約化它即可

 

3.對於《教程11.2 設計的圖機,分別給出執f(32)f(23)像演變過程。

 

4.什么P 類問題?什NP類問題?

 

5.證明兩個m n 二維矩陣相加的題屬於P 類問

 

6.證明含有n 個元素的據序列中求最大素的問題屬於P 問題。

 

7.設計一個確定圖靈機M,用於算后繼函數S(n)=n+1n為一個二進制數),並給出求1010001 后繼函數值的瞬演變過程。

 

1.11.2練習題參考答

 

1.答:B

 

2.答:D

 

3.答:1)執行f(32)時,輸入帶上初始信息為000100B,其瞬像演變過程

 

81

 

 

 

 

算法設計

 

 

下:

 

 

q0000100BB0q30110BBB01q210B

 

Bq100100BBq300110BBB011q20B

 

B0q10100Bq3B00110BBB01q311B

 

B00q1100B

B001q200B

B00q3110B

 

Bq000110B

BBq10110B

BB0q1110B

 

BB0q3111B

BBq30111B

BBq00111B

 

 

BBB1q211B

 

BBB11q21B

 

BBB111q2B

 

BBB11q41B

 

BBB1q41BB

 

 

BBBq41BBB

 

BBBq4BBBB

 

BBB0q6BBB

 

 

最終帶上有一個0,計算結果為1

 

2)執行f(23)時,輸入帶上的初信息為001000B,其瞬像演變過如下:

 

 

q0001000B

 

Bq001000B

 

B0q11000B

 

B01q2000B

 

B0q31100B

 

Bq301100B

 

 

q3B01100BBBq31100B

 

Bq001100BBq3B1100B

 

BBq11100BBBq01100B

 

BB1q2100BBBBq5100B

 

BB11q200BBBBBq500B

 

BB1q3100B

 

 

BBBBBq50B

 

BBBBBBq5B

 

BBBBBBBq6

 

 

最終帶上有零個0,計算結果為0

 

4.答:定性圖靈機以多式時間界可解的題稱為P 類問題用非確定性圖靈機以多項式時間可解的問題稱為NP類問題。

 

5.求兩個m n列的二維矩陣相加的問題對應算法時間復雜度為O(mn),所以屬於P 類問

 

6.答:求含有n個元素的數據列中最大元素的問題的算法時復雜度為O(n),所以屬於P 類問

 

7.解:q0 為初狀態,q3為終止狀態,讀寫頭始時注視最右邊的格。δ 動作函如下:

 

δ(q00)(q11L)

 

δ(q01)(q20L)

 

δ(q0B)(q3BR)

 

δ(q10)(q10L)

 

δ(q11)(q11L)

 

δ(q1B)(q3BL)

 

δ(q20)(q11L)

 

δ(q21)(q20L)

 

δ(q2B)(q3BL)

 

10100010 繼函數值的瞬像變過程如下:

 

 

B1010001q00B

 

B101000q111B

 

B10100q1011B

 

B1010q10011B

 

B101q100011B

 

 

 

B10q1100011B

 

B1q10100011B

 

Bq110100011B

 

q1B10100011B

 

 

 

q3BB10100011B

 

 

其結果為10100011

 

 

82

 

 

 

 

1

 

概論

 

 

1.1212概率法和近似算法

 

1.12.1練習題

 

1.蒙特羅算法是(一種。

 

A.分枝限界算B.貪心算法C.率算法D.算法

 

2.在下算法中有時找不問題解的是()。

 

A.蒙特卡羅算B.拉斯維加算法C.舍伍德算法D.值概率算法3.在下算法中得到的解必正確的是()。

 

A.蒙特卡羅算B.拉斯維加算法C.舍伍德算法D.值概率算法4.總能得非數值問題個解,且所求得解總是正確的是)。

 

A.蒙特卡羅算B.拉斯維加算法C.數值概率算法D.伍德算法5.目前以采用()在項式級時間內求旅行商問題的一近似最優解。

 

 

A.回溯法

 

B.蠻力法

 

C.近似算法D.可能

 

 

6.下列述錯誤的是()。

 

A.概率算法的望執行時間是指反復解同一個輸實例所花的平均行時間

 

B.概率算法的平期望時間是指所輸入實例上的平期望執行時間

 

C.概率算法的最期望事件是指最輸入實例上的期執行時間

 

D.概率算法的望執行時間是指所有輸入實例上所花的平均執行

 

7.下列述錯誤的是()。

 

A.數值概率算一般是求數值計算問題的近似解

 

B.Monte Carlo 求得問題的一個,但該解未必正

 

C.Las Vegas 的一定能求出問題的正確解

 

D.Sherwood 的主要作用是減或是消除好的和的實例之間的差

 

8.近似法和貪心法有什不同?

 

9.給定能隨生成整數1 5 函數rand5(),寫能隨機生成整數1 7 的函數rand7()

 

1.12.2練習題參考

 

1.答:C

 

2.答:B

 

3.答:A

 

4.答:D

 

5.答:C

 

6.答:對概算法通常討論平均的期望時間和壞的期望時間,者指所有輸入實例上平均的期望行時間,后者指壞的輸入實例上期望執行時間。案為D

 

7.答:一旦拉斯維加斯算法找到一個解,那這個解肯定是正的,但有時用拉斯維加斯算法可找不到解。答案C

 

8.答:法不能保證得到最優解。貪心算不一定是近似算,如果可以證明

 

83

 

 

 

 

算法設計

 

決策既不受之前策的影響,也不響后續決策,則心算法就是確定最優解算法。9.解:通過rand5()*5+rand5()產生67892627282930 25個整

 

數,每個整數x 現的概率相等,前面3*7=21 個整,舍棄后面的4 整數,將{678}轉化成1{91011}轉化成2,以此類,即由y=(x-3)/3 最終結果。對應序如下:

 

#include <stdio.h>

 

 

#include <stdlib.h>

 

#include <time.h>

 

intrand5()

 

{int a=1,b=5;

 

return rand()%(b-a+1)+a;

 

}

 

intrand7()

 

{int x;

 

do

 

{

 

x=rand5()*5+rand5();

 

} while (x>26);

 

int y=(x-3)/3;

 

return y;

 

}

 

voidmain()

 

{srand((unsigned)time(NULL));

 

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

 

printf("%d ",rand7());

 

printf("\n");

 

//包含產生隨機數庫函數

 

//一個[1,5]的隨

 

//一個[1,7]的隨//隨機種子

 

//輸出20[1,7]隨機數

 

 

}

 

述程序的一次行結果如圖1.56 示。

 

 

1.56

 

程序執行結果

 

 

84


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM