



1.1第1 章─概论
1.1.1练习题
1.下列关于算法的说法中正确的有()。
Ⅰ.求解某一类问题的算法是唯一的
Ⅱ.算法必须在有限步操作之后停止
Ⅲ.算法的每一步操作必须是明确的,不能有歧义或含义模糊
Ⅳ.算法执行后一定产生确定的结果
A. 1 个B.2 个C.3 个D.4 个
2. T(n)表示当输入规模为n时的算法效率,以下算法效率最优的是()。
A.T(n)= T(n-1)+1,T(1)=1 C.T(n)= T(n/2)+1,T(1)=1
B.T(n)= 2n
D.T(n)=3nlog2n
3.什么是算法?算法有哪些特征?
4.判断一个大于2的正整数n是否为素数的方法有多种,给出两种算法,说明其中一种算法更好的理由。
5.证明以下关系成立:
(1)10n-2n=(n)
(2)2=(2)
6.证明O(f(n))+O(g(n))=O(max{f(n),g(n)})。
7.有一个含n(n>2)个整数的数组a,判断其中是否存在出现次数超过所有元素一半的元素。
8.一个字符串采用string 对象存储,设计一个算法判断该字符串是否为回文。
9.有一个整数序列,设计一个算法判断其中是否存在两个元素和恰好等于给定的整数k。
10.有两个整数序列,每个整数序列中所有元素均不相同。设计一个算法求它们的公共元素,要求不使用STL 的集合算法。
11.正整数n(n>1)可以写成质数的乘积形式,称为整数的质因数分解。例如,12=2*2*3,18=2*3*3,11=11。设计一个算法求n这样分解后各个质因数出现的次数,采用vector 向量存放结果。
12.有一个整数序列,所有元素均不相同,设计一个算法求相差最小的元素对的个数。如序列4、1、2、3的相差最小的元素对的个数是3,其元素对是(1,2),(2,3),(3,4)。
13.有一个map
14.重新做第10题,采用map 容器存放最终结果。
15.假设有一个含n(n>1)个元素的stack









算法设计
1.1.2练习题参考答案
1.答:由于算法具有有穷性、确定性和输出性,因而Ⅱ、Ⅲ、Ⅳ正确,而解决某一类问题的算法不一定是唯一的。答案为C。
2.答:选项A的时间复杂度为O(n)。选项B的时间复杂度为O(n)。选项C的时间复杂度为O(log2n)。选项D 的时间复杂度为O(nlog2n)。答案为C。
3.答:算法是求解问题的一系列计算步骤。算法具有有限性、确定性、可行性、输入性和输出性5 个重要特征。
4.答:两种算法如下:
#include
#include
boolisPrime1(intn)//方法1
{for (int i=2;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)。
(2)2=2*2=(2)。
6.证明:对于任意f1(n)∈O(f(n)),存在正常数c1 和正常数n1,使得对所有n≥n1,有f1(n)≤c1f(n)。
类似地,对于任意g1(n)∈O(g(n)),存在正常数c2 和自然数n2,使得对所有n≥n2,有g1(n)≤c2g(n)。
令c3=max{c1,c2},n3=max{n1,n2},h(n)= max{f(n),g(n)}。
则对所有的n≥n3,有:
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
#include
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
{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
#include
using namespace std;
boolsolve(stringstr)//判断字符串str是否为回文{int i=0,j=str.length()-1;
while (i
{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
#include
using namespace std;
boolsolve(inta[],intn,intk)
{sort(a,a+n);
int i=0, j=n-1;
while (i
{if (a[i]+a[j]==k)
return true;
//递增排序
//区间中存在两个或者以上元素
else if (a[i]+a[j]
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
#include
#include
using namespace std;
voidsolve(set
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
{set
//输出集合的元素
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 b[]={1,2,4,5,3};
int m=sizeof(b)/sizeof(b[0]);
set
set
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 出现的次数,当找到这样质因数后,将(i,ic)作为一个元素插入到vector 容器v 中。最后输出v。对应的算法如下:
#include
#include
using namespace std;
struct NodeType
{int p;
int pc;
//vector向量元素类型 //质因数
//质因数出现次数
};
voidsolve(intn,vector
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
{vector
for (it=v.begin();it!=v.end();++it)
printf("质因数%d出现%d次\n",it->p,it->pc);
}
6








第1章
概论
voidmain()
{vector
int n=100;
printf("n=%d\n",n);
solve(n,v);
disp(v);
}
上述程序的执行结果如图1.5 所示。
图1.5
程序执行结果
12.解:先递增排序,再求相邻元素差,比较求最小元素差,累计最小元素差的个数。对应的程序如下:
#include
#include
#include
using namespace std;
intsolve(vector
//求myv中相差最小的元素对的个数
{sort(myv.begin(),myv.end());//递增排序
int ans=1;
int mindif=myv[1]-myv[0];
for (int i=2;i
{if (myv[i]-myv[i-1]
{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
cout << "相差最小的元素对的个数:" << solve(myv) <<endl; }
上述程序的执行结果如图1.6 所示。
7




图1.6
算法设计
程序执行结果
13.解:对于map
#include
#include
#include
using namespace std;
voidmain()
{map
mymap.insert(pair
mymap.insert(pair
mymap.insert(pair
mymap.insert(pair
mymap.insert(pair
map
map
for (it=mymap.begin();it!=mymap.end();it++)
tmap[(*it).second]++;
map
cout << "求解结果"<<endl;
for (it1=tmap.begin();it1!=tmap.end();it1++)
cout << "" << (*it1).first<< ":" << (*it1).second<< "次\n";
}
上述程序的执行结果如图1.7 所示。
图1.7
程序执行结果
14.解:采用map
#include
#include
using namespace std;
voidsolve(intn,map
{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
{map
for (it=mymap.begin();it!=mymap.end();++it)
printf("质因数%d出现%d次\n",it->first,it->second);
}
voidmain()
{map
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
#include
using namespace std;
intsolve(stack
{stack
int e;
for (int i=0;i
{e=st.top();
st.pop();
tmpst.push(e);
}
e=tmpst.top();
tmpst.pop();
while (!tmpst.empty())
{st.push(tmpst.top());
tmpst.pop();
//出栈第k个元素
//出栈st的k个元素并进tmpst栈//求第k个元素
//将tmpst的所有元素出栈并进栈st9




}
return e;
}
voiddisp(stack
{while (!st.empty())
{printf("%d ",st.top());
st.pop();
}
printf("\n");
}
voidmain()
{stack
算法设计
//出栈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.2第2 章─递归算法设计技术
1.2.1练习题
1.什么是直接递归和间接递归?消除递归一般要用到什么数据结构?2.分析以下程序的执行结果:
#include
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)+n当n>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)+n当n>1
6.采用主方法求解以下题的递归方程。
T(n)=1
当n=1
T(n)=4T(n/2)+n当n>1
7.分析求斐波那契f(n)的时间复杂度。
8.数列的首项a1=0,后续奇数项和偶数项的计算公式分别为a2n=a2n-1+2,a2n+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(n,m)中,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=1,r2=-3,r3=3则递归方程的通解为:H(n)=c1+c2(-3)+c33
代入H(0)=0,有c1+c2+c3=0
代入H(1)=1,有c1-3c2+3c3=1
代入H(2)=2,有c1+9c2+9c3=2
求出:c1=-1/4,c2=-1/12,c3=1/3,H(n)=c1+c2(-3)+c33=(
n‒1
4
+ 13
1
‒。
5.解:构造的递归树如图1.10 所示,第1 层的问题规模为n,第2 的层的子问题的问题规模为n/2,依此类推,当展开到第k+1层,其规模为n/2=1,所以递归树的高度为log2n+1。
第1层有1个结点,其时间为n,第2层有4个结点,其时间为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=4,b=2,f(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)=1,T1(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=2n,则2n-1=m-1,所以有f(m)=f(m-1)+2。
当m 为奇数时,不妨设m=2n+1,则2n-1=m-2,2n=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
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(str,n)返回含n 个字符的字符串str是否为回文,其递归模型如下:
f(str,n)=true
f(str,n)=flase
f(str,n)=f(str+1,n-2)
当n=0 或者n=1 时当str[0]≠str[n-1]时其他情况
对应的递归算法如下:
#include
#include
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
当L≠NULL 时
//包含单链表的基本运算算法
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
当L≠NULL 时
//包含单链表的基本运算算法//逆序输出所有结点值
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(L,x)返回单链表L 中第一个值为x 的结点的地址,其递归模型如下:
f(L,x) = NULL
f(L,x) = L
f(L,x) =f(L->next,x) 对应的递归程序如下:
当L=NULL 时
当L≠NULL 且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(L,x)删除单链表L 中第一个值为x 的结点,其递归模型如下:
f(L,x)≡ 不做任何事情
f(L,x)≡ 删除L 结点,L=L->nextf(L,x)≡ f(L->next,x)
对应的递归程序如下:
当L=NULL
当L≠NULL 且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
当bt≠NULL 且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);//由a和b构造二叉链bt printf("二叉树bt:");DispBTree(bt);printf("\n"); printf("所有叶子结点值之和:%d\n",LeafSum(bt));
DestroyBTree(bt);
//销毁树bt
}
上述程序的执行结果如图1.18 所示。
图1.18
程序执行结果
17.解:设f(bt,k)返回二叉树bt 中所有结点值大于等于k 的结点个数,其递归模型如下:
f(bt,k)=0
f(bt,k)=f(bt->lchild,k)+f(bt->rchild,k)+1f(bt,k)=f(bt->lchild,k)+f(bt->rchild,k)
对应的递归程序如下:
#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
当bt≠NULL 且bt->data≥k其他情况
//包含二叉树的基本运算算法//大于等于k的结点个数
//由a和b构造二叉链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(bt,x,h)返回二叉树bt中x 结点的层次,其中h 表示bt 所指结点的层次,初始调用时,bt 指向根结点,h 置为1。其递归模型如下:
f(bt,x,h)=0
f(bt,x,h)=h
f(bt,x,h) =l
f(bt,x,h) =f(bt->rchild,x,h+1) 对应的递归程序如下:
当bt=NULL
当bt≠NULL 且bt->data=x当l=f(bt->lchild,x,h+1)≠0其他情况
#include "Btree.cpp"
intLevel(BTNode*bt,intx,int h) {//初始调用时:bt为根,h为1
if (bt==NULL) return0;
if (bt->data==x)
return h;
//包含二叉树的基本运算算法//求二叉树bt中x结点的层次
//找到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);
//由a和b构造二叉链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.3第3 章─分治法
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
{int mid=(low+high)/2;
21





算法设计
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(a,0,n-1)递增排序的时间复杂度为O(n)。
6.以下哪些算法采用分治策略:
(1)堆排序算法
(2)二路归并排序算法
(3)折半查找算法
(4)顺序查找算法
7.适合并行计算的问题通常表现出哪些特征?
8.设有两个复数x=a+bi和y=c+di。复数乘积xy 可以使用4 次乘法来完成,即xy=(ac-bd)+(ad+bc)i。设计一个仅用3 次乘法来计算乘积xy 的方法。
9.有4 个数组a、b、c 和d,都已经排好序,说明找出这4 个数组的交集的方法。10.设计一个算法,采用分治法求一个整数序列中的最大最小元素。
11.设计一个算法,采用分治法求x。
12.假设二叉树采用二叉链存储结构进行存储。设计一个算法采用分治法求一棵二叉树bt 的高度。
13.假设二叉树采用二叉链存储结构进行存储。设计一个算法采用分治法求一棵二叉树bt 中度为2 的结点个数。
14.有一种二叉排序树,其定义是空树是一棵二叉排序树,若不空,左子树中所有结点值小于根结点值,右子树中所有结点值大于根结点值,并且左右子树都是二叉排序树。现在该二叉排序树采用二叉链存储,采用分治法设计查找值为x 的结点地址,并分析算法的最好的平均时间复杂度。
22







第1章
概论
15.设有n 个互不相同的整数,按递增顺序存放在数组a[0..n-1]中,若存在一个下标i(0≤i<n),使得a[i]=i。设计一个算法以O(log2n)时间找到这个下标i。
16.请你模仿二分查找过程设计一个三分查找算法。分析其时间复杂度。
17.对于大于1 的正整数n,可以分解为n=x1*x2*…*xm,其中xi≥2。例如,n=12 时有8 种不同的分解式:12=12,12=6*2,12=4*3,12=3*4,12=3*2*2,12=2*6,12=2*3*2,12=2*2*3,设计一个算法求n 的不同分解式个数。
18.设计一个基于BSP 模型的并行算法,假设有p 台处理器,计算整数数组a[0..n-1]的所有元素之和。并分析算法的时间复杂度。
1.3.2练习题参考答案
1.答:C。
2.答:D。
3.答:以a[]={1,2,3,4,5}为例说明。选项A中在查找5 时出现死循环。选项B 中在查找5 时返回-1。选项C 中在查找5 时返回-1。选项D 正确。
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次乘法(即ac、bd 和(a+b)(c+d)乘法运算)。
9.答:采用基本的二路归并思路,先求出a、b 的交集ab,再求出c、d 的交集cd,最后求出ab 和cd的交集,即为最后的结果。也可以直接采用4 路归并方法求解。
10.解:采用类似求求一个整数序列中的最大次大元素的分治法思路。对应的程序如下:
#include
#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(x,n)=x,采用分治法求解对应的递归模型如下:
f(x,n)=x
f(x,n)=f(x,n/2)*f(x,n/2)
f(x,n)=f(x,(n-1)/2)*f(x,(n-1)/2)*x对应的递归程序如下:#include
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
//合并
//由a和b构造二叉链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
若bt≠NULL 且bt 为双分支结点其他情况
//包含二叉树的基本运算算法//求bt中度为2的结点个数
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);
//由a和b构造二叉链bt
printf("二叉树bt:");DispBTree(bt);printf("\n"); printf("bt中度为2的结点个数:%d\n",Nodes(bt));
DestroyBTree(bt);
//销毁树bt
}
上述程序的执行结果如图1.24 所示。
图1.24
程序执行结果
14.解:设f(bt,x)返回在二叉排序树bt 得到的值为x 结点的地址,若没有找到返回空,对应的递归模型如下:
f(bt,x)=NULL
f(bt,x)=bt
f(bt,x)=f(bt->lchild,x)
当bt=NULL
当bt≠NULL 且x=bt->data当x>bt->data
26







第1章
概论
f(bt,x)=f(bt->rchild,x) 对应的程序如下:
#include "Btree.cpp"
当x
//包含二叉树的基本运算算法
BTNode*Search(BTNode*bt,Int x)//在二叉排序树bt查找的值为x结点{if (bt==NULL)return NULL;
if (x==bt->data)return bt;
if (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(bt,x)算法采用的是减治法,最好的情况是某个结点左右子树高度大致相同,其平均执行时间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)/2,若a[mid]=mid 表示找到该元素;若a[mid]>mid说明右区间的所有元素都大于其位置,只能在左区间中查找;若a[mid]
#include
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]
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
intSearch(inta[],intlow,inthigh,intx) {if (high
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;
return Search(a,low,mid1-1,x);
else if (x==a[mid2])
return 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=4,4=2*2
29





算法设计
f(6)=3,分解式为:6=6,6=2*3,6=3*2,即f(6)=f(1)+f(2)+f(3)
以此类推,可以看出f(n)为n 的所有因数的不同分解式个数之和,即f(n)= ∑(/)。对应的程序如下:
#include
#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
{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.4第4 章─蛮力法
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=temp;
}
return dmin;
}
4.给定一个整数数组A=(a0,a1,…an-1),若i<j且ai>aj,则<ai,aj>就为一个逆序对。例如数组(3,1,4,5,2)的逆序对有<3,1>,<3,2>,<4,2>,<5,2>。设计一个算法采用蛮力法求A 中逆序对的个数即逆序数。
5.对于给定的正整数n(n>1),采用蛮力法求1!+2!+…+n!,并改进该算法提高效率。
6.有一群鸡和一群兔,它们的只数相同,它们的脚数都是三位数,且这两个三位数的各位数字只能是0、1、2、3、4、5。设计一个算法用蛮力法求鸡和兔的只数各是多少?它们的脚数各是多少?
7.有一个三位数,个位数字比百位数字大,而百位数字又比十位数字大,并且各位数字之和等于各位数字相乘之积,设计一个算法用穷举法求此三位数。
8.某年级的同学集体去公园划船,如果每只船坐10 人,那么多出2 个座位;如果每只船多坐2 人,那么可少租1 只船,设计一个算法用蛮力法求该年级的最多人数?
9.已知:若一个合数的质因数分解式逐位相加之和等于其本身逐位相加之和,则称这个数为Smith 数。如4937775=3*5*5*65837,而3+5+5+6+5+8+3+7=42,4+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 行:第一行为一个整数n(1≤n≤50),即棋盘的大小,接下来的n 行每行一个字符串表示第i 行棋盘的颜色,'W'表示白色,'B'表示黑色。
输出描述:输出小易会涂画的区域大小。
输入例子:
3
BWW
BBB
BWB
输出例子:
3
11.给定一个含n(n>1)个整数元素的a,所有元素不相同,采用蛮力法求出a 中所有元素的全排列。
1.4.2练习题参考答案
1.答:蛮力法是一种简单直接地解决问题的方法,适用范围广,是能解决几乎所有问题的一般性方法,常用于一些非常基本、但又十分重要的算法(排序、查找、矩阵乘法和字符串匹配等),蛮力法主要解决一些规模小或价值低的问题,可以作为同样问题的更高效算法的一个标准。而分治法采用分而治之思路,把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题直到问题解决。分治法在求解问题时,通常性能比蛮力法好。
2.答:如果用蛮力法求解的问题可以分解为若干个规模较小的相似子问题,此时可以采用递归来实现算法。
3.解:上述算法的时间复杂度为O(n),采用的是最基本的蛮力法。可以先对a中元素递增排序,然后依次比较相邻元素的差,求出最小差,改进后的算法如下:
#include
#include
using namespace std;
intMindif1(inta[],intn)
{sort(a,a+n);
int dmin=a[1]-a[0];
for (int i=2;i
{int temp=a[i]-a[i-1];
if (temp
dmin=temp;
}
return dmin;
}
//递增排序
32






第1章
概论
上述算法的主要时间花费在排序上,算法的时间复杂度为O(nlog2n)。
4.解:采用两重循环直接判断是否为逆序对,算法的时间复杂度为O(n2),比第3章实验3 算法的性能差。对应的算法如下:
intsolve(inta[],intn)
{int ans=0;
for (int i=0;i
for (int j=i+1;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)*n,f(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,有1≤a,d≤5,0≤b,c,e,f≤5,采用6 重循环,求出鸡只数x1=y/2(y 是2的倍数),兔只数x2=z/4(z 是4 的倍数),当x1=x2 时输出结果。对应的程序如下:
#include
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,有1≤a≤9,0≤b,c≤9,满足c>a,a>b,a+b+c=a*b*c。对应的程序如下:
#include
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<12。让y 从1 到100(实际上y取更大范围的结果是相同的)、z从0到11 枚举,求出最大的x 即可。对应的程序如下:
#include
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的各位数字和sum1,以及n的所有质因数的数字和sum2,若sum1=sum2,即为Smitch数。从用户输入的n开始枚举,若是Smitch 数,输出,本次结束,否则n++继续查找大于n 的最小Smitch数。对应的完整程序如下:
#include
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
#define MAXN 51
//问题表示
int n;
char board[MAXN][MAXN];
intgetMaxArea()
{int maxArea=0;
for (int j=0; j
//蛮力法求解算法
for (int i=1; i
countj++;
else
countj=1;
}
36






第1章
概论
if (countj>maxArea)
maxArea=countj;
}
return maxArea;
}
intmain()
{scanf("%d",&n);
for (int i=0;i
scanf("%s",board[i]);
printf("%d\n",getMaxArea());
return 0;
}
11.解:与《教程》中求全排列类似,但需要将求1~n 的全排列改为按下标0~n-1 求a 的全排列(下标从0 开始)。采用非递归的程序如下:
#include
#include
using namespace std;
vector
//存放全排列
voidInsert(vector
{vector
vector
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
vector
s.push_back(a[0]);
ps.push_back(s);
for (int i=1;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
//排列集合元素迭代器