每周学习总结
这周主要学习了递归算法,深度优先搜索(DFS)和广度优先搜索(BFS),递归算法相对比较简单,因为这在第一学期就已经有所学习,所以对递归理解也比较快,而DFS和BFS则让我感到有些理解上的困难,不过这两种算法的大概思路就是枚举所有的可能,并且找出符合的答案。以下通过几道例题对几种算法进行分析。
1求快速幂
typedef long long ll;
ll binaryPow(ll a, ll b){
if(b == 1)
return a;
else if(b % 2 == 1)
return a * binaryPow(a, b - 1) ;
else{
ll num = binaryPow(a, b/2) ; //优化
return num * num ;// 不直接写成return binaryPow(a, b/2, m) * binaryPow(a, b/2, m)
}
这道题是算a^b的解,其实暴力破解,运用一个for循环便能算出答案
… int t=a;
for(int i=1;i<b;i++)
{ a=ta;t=a;}
但是还有更快的方法,那就是运用递归例如当b==5时,
a5=a*(a4)
=a(a2*a2)
=a*((aa)(a*a)
也就是求当a的b次方,b为奇数时转化为a *a^ b-1的值,如果b为偶数则可以转化为a ^ (b/2)*a^(b/2),如果递归到b=1时,则可求出a的b次方的答案,而这个方法可以提升一半的速度。这道题虽然不是很难,但是对于理解递归的思想还是非常有帮助的,递归其实就是将一个很难算的值不断的转换成一个较小的值,通过转换成一个可以简单计算的值,即可逆推出所求的值。
2 dfs例题
#include<cstdio>
#include<iostream>
#include<iomanip>
using namespace std;
int num=0,a[10001]={0},n,r;
bool b[10001]={0};
int search(int); //回溯过程
int print(); //输出方案
int main()
{
cout<<"input n,r:";
cin>>n>>r;
search(1);
cout<<"number="<<num<<endl; //输出方案总数
}
int search(int k)
{
int i;
for (i=1;i<=n;i++)
if (!b[i]) //判断i是否可用
{
a[k]=i; //保存结果
b[i]=1;
if (k==r) print();
else search(k+1);
b[i]=0;
}
}
int print()
{
num++;
for (int i=1;i<=r;i++)
cout<<setw(3)<<a[i];
cout<<endl;
}
这道题要求有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r<n),试列出所有的排列。这道题用了许多全局变量,但在c++中应该尽量减少使用全局变量,因为c++是面向对象的语言(这里不过多赘述),不过在这中算法题中,使用全局变量还是大大减少了代码重复使用。这道题的思路是,首先找第一个我们需要的数,然后找到第二个,直到找到第r个我们需要的数,然后回溯。
先在一条路上走完,然后返回到上一级找下一个数,直到便利完这条路所有可能,然后找其他路径的情况。这道题的思路是典型的dfs算法的思想,通过找第i个数可能的值,然后算出所有的情况,不过原理虽然不是很难,但是我想自己写出这样的代码感觉还是比较困难的。
3 bfs例题
int n;
int main()
{
//int n;
while(cin>>n&&n)
cout<<bfs()<<endl;
return 0;
}
int n;
long long bfs()
{
queue<long long> q;
while(!q.empty()) q.pop();
q.push(1);
while(!q.empty())
{
if(q.front()%n==0) return q.front();
long long p=q.front();
q.pop();
q.push(p*10);
q.push(p*10+1);
}
return 0;
}
这道题要求给出一个整数n,(1 <= n <= 200)。求出任意一个它的倍数m,要求m必须只由十进制的'0'或'1'组成,这道题很罕见的使用了队列(话说做了很多acm的题了,仔细想想很少使用队列),但是在某些时候确意外的很有用,比如这道题,这道题要求出n的一个倍数m,但是要求m必须由0和1组成, 所以一个很显然m的首位必须为1,所以我们首先将1放在队列最前面,然后用一个!q.empty()来判断队列是否为空,我们通过循环来判断排在第一个数能否整除n,如果能,说明找到了答案。不能,我们则去除第一位数,并且将第一位数乘10和乘10+1来获得第二行数(全为1,0组成),第二次循环继续判断第一个数,看是否符合条件。这里贴一张图来更清晰的看出情况的变化.
可以看出,每一行代表着我们要遍历的情况,我们首先找出第一行,是否符合条件,如果不符合,那么就往下一行进行判断。这道题感觉如果不用队列,感觉速度会很慢,并且会很复杂。
每周总结:
本周算是本学期严格意义上acm课的最后一周,不知不觉acm已经上了12周,我在这段时间也慢慢习惯了acm的模式,感觉每周每天都需要做acm的题让我感到十分的有压迫感,而且在每一次做题的时候看不懂题时,内心也是十分的焦灼,但是在每一次靠自己ac一道题时内心也会产生出很大的喜悦,也许这就是算法题的魅力吧,虽然acm课程结束了,但是我也慢慢学会了学习的方法,在接下来的时间,我也会继续学习算法知识,并且让自己变的更强。