算法入门经典第四章 递归和函数

算法竞赛入门经典 第三章函数和递归

1.hash映射
古老的密码:见书本73上页原题,如果有需要pdf电子书的可以私信我拿书
思路:字母可以重复排序,那么关键点就不会是数组上面,那么关键点就是字幕出现的次数
1.遍历原始字符串,把每个字母对应出现的次数用一个数组保存下来,hash映射,
关于映射有许多技巧,需要的在下面留言,或者私信我拿
2.遍历目标串,记录该位置对于往后的那个字母对应的数组减一;(相当于把某一个重排好的字符还原成题目变化前的字符看看是否和原始串一样)
3.flag标志,前面博客有详解,认真看看
代码:

/*
1.输入2个字符串;
2.将第一个字符串存入hash数组,然后遍历第二个字符串
遇到一个字符,该字符后面的那个字符对应的数量减一3
3.遍历一遍,如果为0就是yes,不然就是no; 
JWPUDJSTVP
VICTORIOUS
*/
#include <iostream>
#include <string>
int dp[26];
char s1[10010],s2[10010];
using namespace std;
int main()
{
	string m,n;
	cin>>m>>n;
	int flag=1;
	for(int i=0;i<m.size();++i)
	{
		dp[m[i]-'A']++;
	}
	for(int i=0;i<n.size();++i) 
	{
		dp[n[i]+1-'A']--;
	
	}
	for(int i=0;i<=26;++i)
	{
		if(dp[i]>0) 
		{
			flag=0;
			break;
		}
	 }
	 cout<<(flag?"yes":"no");
 } 

2.递归详解:
递归是什么:
就是每次划分一个问题,把它不断变小,化大为小,化小为无;
比如计算输出 1到10;
就是先打印1,再把2 到 9交给后面打印;
然后打印2,再把3到9交给后面打印;
然后打印3,再把4到9交给后面打印:

递归的条件:
1.拥有递归结束的条件,退出;
2.每次问题规模要变小;
3.每次找出重复的动作;

题目;
组合数:
f=(n!)/(m!)(n-m)!;
n>=m,比如输入n=25,m=12答案是5200300;
注意这边一直乘,大数后就会溢出
一般这种情况都要我们来处理,进行一些约分和化简;
n!/m!
可以写成(1
23mm+1m+2n)/(123n);
约分(m+1
m+2**m+3*…*n);

#include <iostream>
using namespace std;
long long f(long long m,long long n)
{
	long long ans=1;
	for(int i=m+1;i<=n;++i) ans*=i;
	for(int i=1;i<=n-m;++i) ans/=i;
	return ans;
}
int main()
{
	int m,n;
	cin>>m>>n;
	cout<<f(m,n);
 } 

兔子问题;
其实就是斐波那契数列
我们发现每次数的值就是上一次的数值加上 上上次的数值;
推出公式f[n]=f[n-1]+f[n-2];
然后可以直接斐波那契计算;
利用递推,可以把每次出现的数值记录下来,这样就每次输入一个值的时候,就不用重新计算前面的值。利用空间节约时间
总结:
a.递推变化出现数组
b.数组记录计算的值
c.后续不用重复计算

#include <iostream>
using namespace std;
int f[10010];
int main()
{
	int m;
	cin>>m;
	f[1]=1;
	f[2]=1;
	for(int i=3;i<=m;++i)
	{
		f[i]=f[i-1]+f[i-2];
	}
	cout<<f[m];
}

还有一种节约空间的方法:

/*
c=a+b;
a=b;
b=c;
c=a+b;

*/

#include <iostream>
using namespace std;
int f[10010];
int main()
{
	int m,a,b,c;
	cin>>m;
	a=1;
	b=1;
	for(int i=3;i<=m;++i)
	{
		c=a+b;
		a=b;
		b=c;
		
	}
	cout<<c;
}

这样做的好处是节约了数据空间;
但是缺点非常明显,就是增加了重复计算的时间,每次输入一个值要把前面的全部计算出来,才可以推到后面的数值;
一般不推荐;
接下里写几道简答题目理解一下递归:
a.输出1到10;
1.重复的动作:打印数值;
2.规模变小,n加1一;
3。递归出口,n达到10了;



#include <iostream>
using namespace std;

void f(int  n)
{
	if(n==10) return ;//递归出口 
	cout<<n<<endl;//重复动作 
	f(n+1);//规模变化 
}
int main()
{
	f(1);
}

b。计算1加到100;
1.重复动作,加法;
2.问题规模,每次加一;
3。递归出口,n等于100;
代码;



#include <iostream>
using namespace std;

void f(int  n,int sum)
{
	if(n>100)//递归出口 
	{
	cout<<sum;
	return ;
	}
	sum+=n;//重复动作 
	f(n+1,sum);//规模变化 
}
int main()
{
	

	f(1,0);
}

3.字符串反过来输出;
1.重复动作,输出字符;
2,问题规模,每次长度加一;
3.出口,长度小于0;
代码:


#include <string>
#include <iostream>
using namespace std;

void f(string a,int n)
{
	if(n<0) return ;//递归出口;
	cout<<a[n];//重复动作
	f(a,n-1); //问题规模
}
int main()
{
	string a;
	cin>>a;
	f(a,a.size()-1);
}

总结:
递归的想法:一个大问题,可以分成无数个小问题,每个小问题都是重复的操作;直到结束
递归注意:
1.问题规模:每次都要写一个递归函数,函数里面问题的参数要不断变小到后期才会变无;
2.重复动作:每次都是子问题的重复动作,把这个动作抽象成代码,然后写出来
3.递归出口:每个递归的问题都应该有个出口,然后退出,就是问题结束的条件,不然无限递归了;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值