递归
是间接和直接调用自己的算法。
递归三要素:
1.递归主题
2.递归边界
3.递归参数:每次递归发生变化的值,有时为了输入方便,递归的参数会有一部分参数没有发生变化。
每次调用递归就会减小问题规模,递归次数达到一定程度,问题规模就会足够小,小到可以直接求出答案。
举例:
求斐波那契数列前21项,存在数组里。
迭代写法:
void getf1()
{
f[0]=f[1]=1;
for(int i=2;i<=20;i++)
{
f[i]=f[i-1]+f[i-2];
}
}
递归写法:
void getf2(int x)
{
if(x==1||x==0)
return f[x]=1;
return f[x]=getf2(x-1)+getf2(x-2);
}
理解汉诺塔问题
int i=1;//记录步数
void move(int n,char a,char c)//显示移动路径
{
printf(“第%d步:把第%d个盘子%c—>%c\n”,i++,n,a,c);
}
void Hanoi(int n,char a,char b,char c)//汉诺塔递归算法
{
if(n==1) move(n,a,c);//如果只有一个盘子则直接从A柱移到C柱
else
{
Hanoi(n-1,a,c,b);//把A柱n-1个盘子移到B柱
move(n,a,c);//把最后一个盘子从A柱移到C柱
Hanoi(n-1,b,a,c);//把B柱n-1个盘子移到C柱
}
}
int main()
{
int num;
scanf("%d",&num);//输入盘子的个数
Hanoi(num,‘A’,‘B’,‘C’);
return 0;
}
poj 2755神奇的口袋
描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。
输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。
输出
输出不同的选择物品的方式的数目。
样例输入
3
20
20
20
样例输出
3
解题思路:
1.设fun(a1,a2…an,40)为a1,a2…an凑出40的方法数。
考虑凑数的方法分为两类:
有a1 和没有a1,则可以得
fun(a1,…an,40) = fun(a2…an,40-a1) //有a1
- fun(a2…an,40) ;//没a1
我们把a1…an所有整数放到一个数组a中,n表示数组中
整数的个数,i表示从第i个整数开始凑数,sum,表示要凑出
的整数,则可得公式
fun(a,n,i,sum) = fun(a,n,i+1,sum-a[i])//用到a[i] - f(a,n,i+1,sum) ;//不用a[i]
2.递归出口说明
如果sum等于0,说明前面已经找到一种成功的组合方式,返回1,否则,就是sum不等于0
如果i等于n,说明没有可选的数来完成这一组和方式,返回0,如果sum<0,说明刚刚尝试的组合凑不出要组合的数,所以返回0
int a[21] ;
int n ;
int fun(int i,int sum)
{
if(sum等于0)
return 1 ;
if(i等于n||sum<0) return 0 ;
return fun(i+1,sum-a[i]) + fun(i+1,sum) ;
}
int main()
{
int i,t;
cin>>n ;
for(i=0;i<n;i++)
cin>>a[i] ;
t = fun(0,40);
cout<<t<<endl;
return 0 ;
}
整数划分
整数划分问题是算法中的一个经典命题之一,有关这个问题的讲述在讲解到递归时基本都将涉及。所谓整数划分,是指把一个正整数n写成如下形式:
n=m1+m2+…+mi,其中1<=mi<=n,则称{m1,m2,…,mi}为n的一个划分。如果{m1,m2,…,mi}最大值不超过m,即max(m1,m2,…,mi)<=m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m)。例如n=4时,有{4},{3,1},{2,2},{2,1,1},{1,1,1,1}这样的5个划分则f(n,4)=5。下面将用递归法求f(n,m):
a)当等于1时,不论m何值,都只有一种划分{1};
b)当m等于1时,不论n何值,都只有一种划分{1,1,…,1};
c)当n等于m时,
分划分中包含n时只有一种;
当划分中不包含n即最大值小于m时有f(n,m-1)种;
故f(n,n)=1+f(n,n-1);
d)当n<m时,类似f(n,n)
e)当n>m,这是最一般情况,
若划分中包含m,即{{x1,x2,…,xi},m},x1+x2+…+xi=n-m,则f(n,m)=f(n-m,m);
若划分中不包含m,即划分中值都比m小,f(n,m)=f(n,m-1)
故总数 f(n, m) = f(n-m, m)+f(n,m-1)
则递归模型:f(n,m)=1 when n等于1 or m等于01
f(n,m)=f(n,n) when m>n
f(n,m)=1+f(n,m-1) when m等于n
f(n,m)=f(n-m,m)+f(n,m-1) when m<n
int splitint(int n,int m)
{
if(n= =1||m= =1)return 1;
if(n<m)return splitint(n,n);
else if(n==m)return (1+splitint(n,m-1));
else return(splitint(n-m,m)+splitint(n,m-1));
}
分治
概念:
把一个任务,分成形式和原任务相同,但规模更小的 几个部分任务(通常是两个部分),分别完成,或只 需要选一部完成。然后再处理完成后的这一个或几个 部分的结果,实现整个任务的完成。
典型应用:归并排序、快排
在day2-1有归并以及快排代码。
二分
猜一个范围内的数。
在包含size个元素的、从小到大排序的int数组a里查找元素 p,如果找到,则返回元素下标,如果找不到,则返回-1。要求复杂度O(log(n))
int BinarySearch(int a[],int size,int p)
{
int L = 0; //查找区间的左端点
int R = size - 1; //查找区间的右端点
while( L <= R) //如果查找区间不为空就继续查找
{
int mid = L+(R-L)/2; //取查找区间正中元素的下标
if( p == a[mid] )
return mid;
else if( p > a[mid])
L = mid + 1; //设置新的查找区间的左端点
else
R = mid - 1; //设置新的查找区间的右端点
}
return -1;
}//复杂度O(log(n))
为了防止 (L+R)过大溢出:
int mid = L+(R-L)/2;
STL
直接给个知识挺全的网站吧
STL详解