First--改正小错误
首先,在每次的编程中,我都会有一些小错误发生,虽然错误较小,但仍然致命,那么,为了改正这些有伤分数的地方,有以下几个习惯需要我进行养成。
Num.1:
在使用局部变量时,应注意
①是否与全局变量有冲突。
②是否与另一个局部变量搞混。
③是否将不为零的局部变量当0用。
Example:
1.
Using namespace std;
Int i;
Int main()
{
Int i;
}
2.
Using namespace std;
Int a()
{
Int i;
........//不关i啥事
Cout<<i;
}
Int main()
{
Int i;
.......
i=0;
a();
return 0;
}
说明:在上面这个样例中,i在main被定义了的,而在a中也有一个i,虽然它们不会互相影响,但是,很明显可以看出,作者把这两个i混为一谈,因为在a中,i是一个随机数,所以这里又犯②错误又犯③错误。
Num.2:
括号中的等于号不注意。
Example:
1.
If(i=1)
Num.3:
未打return 0
Example:
1.
Using namespace std;
Int main()
{
.......
}
说明:这个很重要,因为在noip和noi中,没打return 0就视为程序不正常退出,因此,这个必须要加以注意。
Second---算法详解篇
上千个题就有上万种算法,那么,作为编程中最重要的一环,就需要仔细再仔细,不能有丝毫马虎大意。
Num.1:
图一直是OI中重要的一部分,算法也有搜索,dijkstra,bellman -ford等,此节将对于图的运算进行一番讲解。
储存图类型:
一共有2种方法对待一张图,分别是邻接链表与邻接矩阵,各有千秋,但是,用的最多的还是空间需求少的邻接链表,下面是解析与实现方法。
邻接矩阵:
邻接矩阵十分简单易懂,方法就是开一个n*n的二维数组,然后用map[a][b]=1来表示a点与b点之间有一条边。也可以将map[a][b]=v来表示a到b要v的代价。
邻接矩阵必须放在全局变量中,否则便要在开头就花n*n的时间来初始化数组,邻接矩阵的好处便是操作简单,修改简单,查询速度快,然而占内存实在是太大了,因此不会怎么去用它。
邻接链表:
邻接链表比起邻接矩阵而言复杂一些,方法是这样的:开一个结构体,然后输入点a,点b,以及花费。
结构体写法如下:
Struct 变量名
{
int 子变量(包括花费,去的点);
};
调用时就用 变量名.子变量
邻接链表写法如下:
Struct 变量名[n]
{
Int cost,to;
};
写入时就写:
Cin>>当前要输入的定点编号(a)>>可以去的顶点(b)>>花费(v);
变量名[a].cost=v,变量名[a].to=b
关于图的算法:
每种图都有属于TA的特点,因此,图的算法就是对这种情况进行不同的考虑与思索。
其中,最容易理解的是Floyd算法,但由于其必须用邻接矩阵来存储图类型,且耗时太大,所以只能用来打暴力;
其次,便是Bellman算法,不过,他可以优化成Dijkstra算法,各有优劣,bellmanO(点数X边数)适合于边数较小的情况,Dijkstra O(点数X点数)适合于边数较大的情况。
Floyd:
这个算法适用于任何暴力算法,从上到下,从左到右,从里到外,都可以。下面是实现方法:
Int map[n][n];(记录是否有边和权值是多少,没有值时为INF,map[1][1]=0)
For(int i=1;i<=n;i++)
For(int j=1;j<=n;j++)
For(int k=1;k<=n;k++)
{
Map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
}
Dijkstra:
这个程序是大多数图的方法,其中还可以加一点优化和修改。操作步骤为此几点:
①首先用邻接矩阵或链表存下来这张图,然后开一个大小为顶点数的数组(一维)代表这个顶点到原点的距离,初始化为无穷大,再开一个这种数组,代表是否这个顶点已被搜过。
②将原点距离赋值为0,并标记为已搜。
③搜索每一个点,看是否有点满足:①此节点尚未用过,②与原点距离是最小的(解释:因为与原点距离最小的话就可以减少每次更改次数,因为原本这个距离就是最小的,与另一个点的距离就变成了它距离原点的距离+它距离那个点的距离)
④假如没有,说明所有点已搜完,退出
⑤若是有,则将它标记为已搜过
⑥对所有点重新进行寻找,若是有的点的花费可以更少,就更新
⑦若是没退出,重复至③
Num.2:
数论一直是OI考试中的一个重点,其内容夹杂万千,如辗转相除法,素数算法,模运算等等。
辗转相除法:
辗转相除法是一个重点大法,其内容可以涵盖许多题目,实现起来也非常容易,但想到很难。函数如下:
Int gcd(int a,int b)//(求a和b的最大公约数,且a>b)
{
If(b==0)
return a;
return gcd(b,a%b);
}
算法的意义为:如果b已经到了不能除的地步(也就是0),那么a就是原本两个数的最大公约数,否则,a就变成b/a的余数。
素数算法:
素数算法有多种用途,也有多种解释,其中,最常见的用途便是素数的判定。由此可以扩展到埃氏筛法,以及区间筛法。
素数判定:
素数判定是一个十分简单的内容,只要判断其是否为素数就行了。同时由于判定范围较小,可以很轻松地在时间范围内完成。代码如下:
Pd=0;//判断n是否为素数
For(int i=2;i*i<=n;i++)
{
If(n%i==0)
Pd=1,break;//代表n不是素数
}
埃氏筛法:
这种素数判定方法适用于那些判定书很多的那些题,首先先找到它们的最大值,然后2-max进行一次搜索,从2开始,若是没有划去的数都是素数,然后在2-n中将其倍数划去。
Bool ss[MAX_N];//假设已经在这里面输入完成,且max为其中最大值,false代表是素数,true代表不是
Int main()
{
Int max;
For(int i=2;i<=max;i++)
{
If(bool[i]==false)
{
For(int j=i;j<=max;j+=i)
{
Bool[j]=true;
}
}
}
区间筛法:
区间筛法是素数判定中的一种,从a到b,只不过不是很难,代码和素数判定一样,只是范围有所不同罢了。
模运算:
这一章十分简单,只需要知道%这个字符就行了
Num.3:
字符串也一直是OI的重要考点之一,其内容变幻万千,上能3和6,下能1和4,中间还可以2和5.不过,字符串的意义就是:恶心你。无数种特殊情况,一连串的if能让人摸不着头脑。However,究其根本,它只是一种输入方法罢了,只是要你把它数字化后再进行运算,一切就迎刃而解了。
Num.4:
排序作为OI考试中重要(坑时间复杂度)的一环,难倒了许多考生,希尔排序,插入排序,快速排序等等。
快速排序:
可以说是排序之中最简单的了,速度也是很快的,美中不足的是它不能在排序的时候对每一个元素做出改变。在使用时,于开头出打出algorithm头文件,
在排序时用sort(数组名+1,数组名+n+1);
希尔排序
Third------数据结构篇
数据结构与算法一直是比赛中相辅相成的部分,它们有着密切的关系,因为某些算法只能在特定的数据结构中使用,如线段树,等等。
Num.1:
二叉搜索树是二叉树的一种,他始终遵循着左儿子小,右儿子大这一规则,是深搜与广搜的实现部分。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int ec[105],n,a,zz;//定义一个树ec,树的指针zz,输入元素的个数n,元素统一用a输入
int sc(int zz)
{
if(ec[zz*2]==0&&ec[zz*2+1]==0)//如果它无儿无女,就直接删
ec[zz]=0;
else if(ec[zz*2]==0&&ec[zz*2+1]!=0)//如果它没左儿子 却有右儿子,就让它的右儿子上来
{
ec[zz]=ec[zz*2+1],ec[zz*2+1]=0;
sc(zz*2+1);//上来了以后右儿子也是空了,就又要从他的右儿子的儿孙之中找一个上来
}
else
{
int k=zz;
k=k*2;
while(ec[k*2+1]!=0)//不然就从它的左儿子子孙中找到最大的一个
k=k*2+1;
ec[zz]=ec[k];
ec[k]=0;
sc(k);//给他的空位善后
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)//插入数值
{
zz=1; //将指针定义为树的根节点
cin>>a;
while(ec[zz]!=0)//当当前指针所指的对象不为空,也就是这里有元素时,继续寻找
{
if(a<=ec[zz])//当这个新元素小于当前所指节点时,它去左儿子路线
zz*=2;
else
zz=zz*2+1;//不然去右儿子路线
}
ec[zz]=a;//存放元素
}
int jsq=1;//打印二叉搜索树
for(int i=1;i<=n*2+1;)
{
for(int j=1;j<=jsq;j++,i++)
cout<<ec[i]<<" ";
jsq*=2;
cout<<endl;
}
//删除数值
int cc,sz;
cin>>cc;
for(int i=1;i<=cc;i++)
{
cin>>sz;
zz=1;
while(sz!=ec[zz])//删除的第一步是找到要删除数值的编号
{
if(sz>ec[zz])
zz=zz*2+1;
if(sz<ec[zz])
zz*=2;
}
sc(zz);
}
jsq=1;//打印二叉搜索树
for(int i=1;i<=n*2+1;)
{
for(int j=1;j<=jsq;j++,i++)
cout<<ec[i]<<" ";
jsq*=2;
cout<<endl;
}
//查找数值
int q,p;
int pd;
cin>>p;
for(int i=1;i<=p;i++)
{
cin>>q;
pd=0;
zz=1;
while(q!=ec[zz])//当它没有到达位置时
{
if(q>ec[zz])//假如这个数值大于当前编号,就去右儿子
zz=zz*2+1;
if(q<ec[zz])//不然去左儿子
zz*=2;
if(q!=ec[zz]&&(ec[zz*2]==0&&ec[zz*2+1]==0)||(q>ec[zz]&&ec[zz*2+1]==0)||(q<ec[zz]&&ec[zz*2]==0))
/*当它的左儿子与右儿子都没有时且他还不等于当前指针时,它就不在这个树中存在;又若是它不等于当前编号
,且它大于这个编号却没有儿子时 他也不存在;再若是它小于这个编号,却没有左儿子时,也不在这个树中存在
*/
{
pd=1;
break;
}
}
if(!pd)
{
cout<<"Yes "<<zz<<endl;
}
else
{
cout<<"No "<<endl;
}
}
}
Num.2:
栈也是OI中的一个重要考点 ,根据不同需要,可以被深搜,广搜,动态规划等算法运用.
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int s[10005],top;//建立栈的数组s,再建立一个指针TOP来指出栈顶的元素
int main()
{
int n;
//压入n元素
while(cin>>n)
{
top++;
s[top]=n;
}
//弹出第一个元素
cout<<s[top];
s[top]=0;
top--;
}