Computer program---我的OI之路

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;

}

说明:在上面这个样例中,imain被定义了的,而在a中也有一个i,虽然它们不会互相影响,但是,很明显可以看出,作者把这两个i混为一谈,因为在a中,i是一个随机数,所以这里又犯②错误又犯③错误。

Num.2:

括号中的等于号不注意。

Example:

1.

If(i=1)

Num.3:

未打return 0

Example:

1.

Using namespace std;

Int main()

{

.......

}

说明:这个很重要,因为在noipnoi中,没打return 0就视为程序不正常退出,因此,这个必须要加以注意。

                Second---算法详解篇

  上千个题就有上万种算法,那么,作为编程中最重要的一环,就需要仔细再仔细,不能有丝毫马虎大意。

Num.1

  图一直是OI中重要的一部分,算法也有搜索,dijkstrabellman -ford等,此节将对于图的运算进行一番讲解。

储存图类型:

  一共有2种方法对待一张图,分别是邻接链表与邻接矩阵,各有千秋,但是,用的最多的还是空间需求少的邻接链表,下面是解析与实现方法。

 

邻接矩阵:

  邻接矩阵十分简单易懂,方法就是开一个n*n的二维数组,然后用map[a][b]=1来表示a点与b点之间有一条边。也可以将map[a][b]=v来表示abv的代价。

  邻接矩阵必须放在全局变量中,否则便要在开头就花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];(记录是否有边和权值是多少,没有值时为INFmap[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)//(ab的最大公约数,且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;

}

}

}

区间筛法:

  区间筛法是素数判定中的一种,从ab,只不过不是很难,代码和素数判定一样,只是范围有所不同罢了。

模运算:

  这一章十分简单,只需要知道%这个字符就行了

Num.3:

  字符串也一直是OI的重要考点之一,其内容变幻万千,上能36,下能14,中间还可以25.不过,字符串的意义就是:恶心你。无数种特殊情况,一连串的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--;

}

   

猜你喜欢

转载自blog.csdn.net/PUBG___/article/details/80777814
今日推荐