基本算法C版本.docx
- 文档编号:10523177
- 上传时间:2023-02-17
- 格式:DOCX
- 页数:52
- 大小:44.85KB
基本算法C版本.docx
《基本算法C版本.docx》由会员分享,可在线阅读,更多相关《基本算法C版本.docx(52页珍藏版)》请在冰豆网上搜索。
基本算法C版本
基本算法(C++版本)
还没有全部转成C++,将就点吧,快了
说明:
红色的代码可以只看懂不背
这是基本算法的C++版本,里面可能涉及一些C的函数,说明一下C++可以兼容所有的C那是因为C++是在C的基础上扩展的一门语言,我们这里没有用到C++的类,其实ACM很少用到类的,我们几乎所有的程序都是面向过程的(非面向对象)。
所以不懂C语言的同学,就当作多学了几个函数的使用,不懂的就查msdn,或者问同学,我也是刚刚学C++和C,所以~~
排序
快速排序难度系数:
**
快速排序的思想是:
先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1,处理结束.(必考,常用)
例:
输入一组数据小到大排序.
程序1:
#include
inta[100];
intn;
voidquicksort(ints,intt)//总的思想就是第s个节点派到它最后应该排的位置;
{
inti,j,x,t1;
i=s;j=t;x=a[i];
while(i { while((a[j]>=x)&&(j>i))j=j-1; if(j>i){t1=a[i];a[i]=a[j];a[j]=t1;} while((a[i]<=x)&&(i if(i } a[i]=x; i=i+1;j=j-1; if(s if(i } intmain() { while(EOF! =scanf("%d",&n))//也可以是这样写: while(EOF! =0) {{ for(intp=0;p scanf("%d",&a[p]);for(intp=0;p quicksort(0,n-1);scanf("%d",&a[p]); for(p=0;p printf("%d",a[p]);}for(p=0;p }printf("%d",a[p]); return0;} } 快速是经常用的,要自己多多练习,最好是自己给自己几个题目,做到不看书都可以写出来。 堆排序难度系数: *** 堆: 设有数据元素的集合(R1,R2,R3,...Rn)它们是一棵顺序二叉树的结点且有 Ri<=R2i和Ri<=R2i+1(或>=) 堆的性质: 堆的根结点上的元素是堆中的最小元素,且堆的每一条路径上的元素都是有序的。 堆排序的思想是: 1)建初始堆(将结点[n/2],[n/2]-1,...3,2,1分别调成堆) 2)当未排序完时 输出堆顶元素,删除堆顶元素,将剩余的元素重新建堆。 程序如下: #include #include #include inta[101]; intn,i; voidsift(intl,intm) { inti,j,t; i=l;j=2*i;t=a[i]; while(j<=m) { if((j if(t>a[j]) { a[i]=a[j];i=j;j=2*i;//注意i和j已经改变了 } elsebreak; a[i]=t; } } voidmain() { while(EOF! =0) { scanf("%d",&n);//输入的数组元素个数 for(i=1;i<=n;i++) scanf("%d",&a[i]);//输入数组的元素 for(i=div(n,2).quot;i>=1;i--) sift(i,n);//思考为什么要对第(ndiv2)个元素开始排序? 具体自己写出测试的数据,跟踪,如果一个节点左右子节点都比它大,就什么都不做了,但是最小的那个可能在子节点的子,这样就达不到选择最小的目的,所以我们要保证当前要处理的节点的两棵子树已经完成了最小节点在根的工作。 完成了以上的for循环之后我们才算完成了对排序,这个时候我们不能保证从第一层往下一个个的节点列出来结果是从小到大的,(注意,对排序只能保证根节点是最小的,这个对于任何一棵子树都适应),所以如果你要得到从小到大的结果就要不断的保存最小的结果(根) for(i=n;i>=2;i--) { printf("%d",a[1]); a[1]=a[i];//第一个已经最小了,我们要做的工作是处理剩下元素,所以把最后的一个元素覆盖第一个,然后排序. sift(1,i);//这个时候第一个根节点的左右子树已经标准的堆排序树了,也就是说只要对第一个节点进行堆排序就行了,这个时候就要好好研究上面sift(vara: arr;l,m: integer)函数的具体跟踪工作了。 } printf("%d\n",a[1]); } } 堆排序有两个比较明显的优点: 优点1.只要排好了之后,以后要找到最小的(或是最大的),只要在根的两个子节点找,但是如果你只是要找到不难两个选一个就行了,但是你如果还要继续找剩下最小的啊,所以你不能一走了之,你应该把当前的子节点(刚到根的最后的节点)派到它应该在的位置,为的就是达到堆排序树的状态,方便下来的找最小的节点。 优点2.实现动态排序,如果其中一个节点被改变了,我们可以让它派到适当的位置,当然初学可能体会不了这一点。 简单的搜索 搜索没有加截支就是傻瓜搜索,基本没有这样的搜索谓的截支就是避免不必要的搜索);几乎所有的搜索都跟宽度或深度有关,本人认为搜索加截支就是回溯了。 所以20个算法里面没有回溯。 题目难度系数: *** PrblemAChessGuess A、B、C、D四名选手进行象棋比赛,赛前甲、乙、丙、丁四位棋迷对选 手名次作了预测,他们的预测数据存储在文件guess.in中,每人的数据放一行, 每行含四个数,分别表示A、B、C、D的名次,各个数用空格隔开,0代表不预 测。 如甲的数据若为: 2431表示甲预测A第2名、B第4名、C第3名、D第 1名。 又如乙的数据若为: 0304表示乙预测B第2名、D第4名,不对A、C 作预测。 若某棋迷猜中了n个选手的名次,称其成绩为n,比赛结束后发现各棋 迷的成绩一样,此成绩存放在guess.in的第5行,请编程根据以上数据求出A、 B、C、D的名次,用空格隔开写成文件guess.out的一行,例如若guess.in内容 为 : 3104 2431 1024 1342 1 则guess.out内容为: 2314 #include #include intmain() { inta,b,c,d,n,i; inta1[4]; intb1[4]; intc1[4]; intd1[4]; while (1)//(EOF! =0) { for(i=0;i<4;i++) { cin>>a1[i]>>b1[i]>>c1[i]>>d1[i]; if(cin.eof())return0; //scanf("%d%d%d%d\n",&a1[i],&b1[i],&c1[i],&d1[i]); } //scanf("%d\n",&n); cin>>n; for(a=1;a<=4;a++) { for(b=1;b<=4;b++) { if(a! =b) { for(c=1;c<=4;c++) { if((a! =c)&&(b! =c)) { d=10-a-b-c; if((((a==a1[1])+(b==b1[1])+(c==c1[1])+(d==d1[1]))==n)&& (((a==a1[2])+(b==b1[2])+(c==c1[2])+(d==d1[2]))==n)&& (((a==a1[3])+(b==b1[3])+(c==c1[3])+(d==d1[3]))==n)&& (((a==a1[0])+(b==b1[0])+(c==c1[0])+(d==d1[0]))==n)) { cout< }//printf("%d%d%d%d\n",a,b,c,d); } } } } } } return0; } 题目难度系数: ***** 数码难题: 283 123 164->8 4 7 5 765 要求输出步数(其实可以输出整个过程的呢) 程序如下: programnum8; uses SysUtils; typea33=array[1..3,1..3]of0..8; a4=array[1..4]of-1..1; node=record ch: a33; si,sj: 1..3; pnt,dep: byte; end; constgoal: a33=((1,2,3),(8,0,4),(7,6,5)); start: a33=((2,8,3),(1,6,4),(7,0,5)); di: a4=(0,-1,0,1); dj: a4=(-1,0,1,0); vardata: array[1..100]ofnode;//用以保存产生得状态 temp: node; r,k,ni,nj,head,tail,depth: integer; functioncheck(k: integer): boolean;//是否可以产生下一个状态 begin ni: =temp.si+di[k];nj: =temp.sj+dj[k]; if(ni>=1)and(ni<=3)and(nj>=1)and(nj<=3)thencheck: =trueelsecheck: =false; end; functiondupe: boolean;//判断是否和已经产生的状态中复 vari,j,k: integer; buf: boolean; begin buf: =false;i: =0; repeat inc(i);buf: =true; forj: =1to3do fork: =1to3do ifdata[i].ch[j,k]<>data[tail].ch[j,k]thenbuf: =false; untilbufor(i>=tail-1); dupe: =buf; end; functiongoals: boolean;//判断是否为目标状态 vari,j: byte; begin goals: =true; fori: =1to3do forj: =1to3do ifdata[tail].ch[i,j]<>goal[i,j]thengoals: =false; end; proceduresolve;//这里有一种宽度搜索的思想 begin whilehead<=taildo begin inc(head);temp: =data[head]; depth: =temp.dep; forr: =1to4do ifcheck(r)then begin inc(tail);data[tail]: =temp; withdata[tail]do begin ch[si,sj]: =ch[ni,nj];ch[ni,nj]: =0;si: =ni;sj: =nj;pnt: =head;dep: =depth+1; end; ifdupethendec(tail) else ifgoalsthen begin writeln(data[tail].dep); //readln;//如果要看结果就去掉//readln前面得”//” exit; end; end; end; end; begin head: =0;tail: =1; withdata[1]do begin ch: =start;si: =3;sj: =2;pnt: =0;dep: =0; end; solve; end. 这道题是在这里算难的了,理解可以在脑海中构造一棵搜索树(四叉树,一个点有四个子点)。 简单的动态规划: “练好动态规划就练好了内功”,呵呵,动态规划确实是好东西。 这里我们只是要求对动态规划(dynamicprogramming简称dp)有个基本的了解。 【例题】难度系数: ** 数字三角形问题。 7 38 810 2774 55265 示出了一个数字三角形宝塔。 数字三角形中的数字为不超过100的正整数。 现规定从最顶层走到最底层,每一步可沿左斜线向下或右斜线向下走。 假设三角形行数≤100,编程求解从最顶层走到最底层的一条路径,使得沿着该路径所经过的数字的总和最大,输出最大值。 输人数据: 由文件输入数据,文件第一行是三角形的行数N。 以后的N行分别是从最顶层到最底层的每一层中的数字。 如输入: 5 7 38 810 2774 45265 输出: 30 【分析】对于这一问题,很容易想到用枚举的方法(深度搜索法)去解决,即列举出所有路径并记录每一条路径所经过的数字总和。 然后寻找最大的数字总和,这一想法很直观,很容易编程实现其程序(这里采用逆推的方法)如下: #include #include intmain() { inta[101][101]; intf[101]; intn,i,j; while(EOF! =0) { cin>>n;//scanf("%d",&n); for(i=1;i<=n;i++) { for(j=1;j<=i;j++) { cin>>a[i][j];//scanf("%d",&a[i,j]); } } for(i=1;i<=n;i++)f[i]=a[n][i]; for(i=n-1;i>=1;i--)//从第n-1层算到第一层 { for(j=1;j<=i;j++) { if(f[j]>f[j+1]) f[j]=a[i][j]+f[j]; else f[j]=a[i][j]+f[j+1]; } } cout< } return0; } 每层刷新f的值,f1到fi就是~~好像你从上往下,到了第i层,那个a[I,j]节点对你说,选我吧,我最大的值(经过我可以捡到最大的值)就是fj,反正就是下层向上层报告同样的事情,最后a[1,1]只需要问它的两个“下属”-a[2,1]和a[2,2]: “选你们那个好? ” 没得说,自己试试,不难理解。 用动态规划解题主要思想是用空间换时间.我们增加的空间是用来保存有用的中间结果的,往往避免了重新计算一遍,剩下了很多时间啊,在这里我们浪费一些空间很值得,ACM中往往注重的时间,要求快。 让我们再来看一道题: (同样是保存有用的中间结果,这个结果要求没有后效性-就是不会影响到后面应该做出的决策,这点也决定了题目能否用动态规划解) 【例题】最长不降子序列(经典习题) 设有由n个不相同的整数组成的数列,记为: a (1)、a (2)、……、a(n)且a(i)<>a(j) (i<>j) 例如3,18,7,14,10,12,23,41,16,24。 若存在i1 如上例中3,18,23,24就是一个长度为4的不下降序列,同时也有3,7,10,12,16,24长度为6的不下降序列。 程序要求,当原数列给出之后,求出最长的不下降序列。 (2)算法分析 根据动态规划的原理,由后往前进行搜索。 1·对a(n)来说,由于它是最后一个数,所以当从a(n)开始查找时,只存在长度为1的不下降序列; 2·若从a(n-1)开始查找,则存在下面的两种可能性: ①若a(n-1) ②若a(n-1)>a(n)则存在长度为1的不下降序列a(n-1)或a(n)。 3·一般若从a(i)开始,此时最长不下降序列应该按下列方法求出: 在a(i+1),a(i+2),…,a(n)中,找出一个比a(i)大的且最长的不下降序列,作为它的后继。 4.用数组b(i),c(i)分别记录点i到n的最长的不降子序列的长度和点i后继接点的编号 (3)程序如下: (逆推法) 10 318714101223411624 programProject2; uses SysUtils; constmaxn=100; vara,b,c: array[1..maxn]ofinteger; //b[i]: 代便第i个数和它前面的数所能组成的最长不降子序列的个数; //c[i]: 代便第i个数和它前面的数组成的最长不降子序列时最靠近它的那个数的位置; n,i,j,max,p: integer; begin whilenoteof(input)do begin readln(n); fori: =1tondo begin read(a[i]);b[n]: =1;c[n]: =0; end; fori: =n-1downto1do begin max: =0;p: =0; forj: =i+1tondo if(a[i]max)then begin max: =b[j];p: =j end; ifp<>0then begin b[i]: =b[p]+1;c[i]: =p; end end; max: =0;p: =0; fori: =1tondo ifb[i]>maxthen begin max: =b[i];p: =i; end; writeln(max); write('resultis: '); whilep<>0do begin write(a[p]: 5);p: =c[p]; end; end; end. b[i]: 代便第i个数和它前面的数所能组成的最长不降子序列的个数(包含自身); c[i]: 代便第i个数和它前面的数组成的最长不降子序列时最靠近它的那个数的位置; 砍材不误磨刀,我们花点时间来说。 就这个例子318714101223411624 3说: 我前面没有人比我小啊,所以我的最多不降个数b[1]为1(就是我自己拉),所以我没有最佳的前驱,既是c[1]为0; 18就说: 我前面比我小的只有一个,所以b[2]为2,c[2]为1; 7就说: 我前面比我小的只有3,所以b[3]为2,c[3]为1; 14就说: 我前面比我小的有两个3和7,那么选那个作为我的最佳前驱呢? 那就要看选那个比较合算了,当然是选7啦,选了7我的最长不降子序列可以长点,b[4]为3,c[3]为3; 下来我就不说了,再说就有人说我傻了――~~~ 总支就是不断运用以前计算的结果,可以说动态规划的这个优点很大程度上祢补了递归算法的不足。 以上两道题都是必懂的。 好好体会,说出算法的精华部分就算你成功一半,另一半要你会运用(不过你至少以后看到这样的题目五分钟内搞定它) 【例题】完全背包问题(经典)难度系数: *** 一个旅行者有一个最多能用m公斤的背包,现在有n种物品,每件的重量分别是W1W2,...,Wn,每件的价值分别为C1,C2,...,Cn.若的每种物品的件数足够多.求旅行者能获得的最大总价值。 本问题的数学模型如下: 设f(x)表示重量不超过x公斤的最大价值, 则f(x)=max{f(x-i)+c[i]} 当x>=w[i]1<=i<=n 可使用递归法解决问题程序如下: programProject2; {$APPTYPECONSOLE} uses SysUtils; constmaxm=200;maxn=30; varm,n,j,i,t: integer; c,w: array[0..maxn]ofinteger; functionf(x: integer): integer; vari,t,m: integer; begin ifx=0thenf: =0else begin t: =-1; fori: =1tondo begin ifx>=w[i]thenm: =f(x-i)+c[i]; ifm>tthent: =m; end; f: =t; end; end; begin whilenoteof(input)do begin readln(m,n); fori: =1tondo readln(w[i],c[i]); writeln(f(m)); end; end. 说明: 当m不大时,编程很简单,但当m较大时,容易超时,上面的程序有理论价值,但是效率不好! 所以上面的程序可以不背,关键是下面的这个改进的递归法 改进的的递归法的思想还是以空间换时间,这只要将递归函数计算过程中的各个子函数的值保存起来,开辟一个一维数组即可 程序如下: programProject2; uses SysUtils; constmaxm=2000;maxn=30; var m,n,j,i,t: integer; c,w: array[0..maxn]ofinte
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 基本 算法 版本