经典算法实例.docx
- 文档编号:26790759
- 上传时间:2023-06-22
- 格式:DOCX
- 页数:34
- 大小:23.89KB
经典算法实例.docx
《经典算法实例.docx》由会员分享,可在线阅读,更多相关《经典算法实例.docx(34页珍藏版)》请在冰豆网上搜索。
经典算法实例
CC++,经典算法实例
一、数论算法
1.求两数的最大公约数
functiongcd(a,b:
integer):
integer;
begin
ifb=0thengcd:
=a
elsegcd:
=gcd(b,amodb);
end;
2.求两数的最小公倍数
functionlcm(a,b:
integer):
integer;
begin
ifa lcm: =a; whilelcmmodb>0doinc(lcm,a); end; 3.素数的求法 A.小范围内判断一个数是否为质数: functionprime(n: integer): Boolean; varI: integer; begin forI: =2totrunc(sqrt(n))do ifnmodI=0thenbegin prime: =false;exit; end; prime: =true; end; B.判断longint范围内的数是否为素数(包含求50000以内的素数表): proceduregetprime; var i,j: longint; p: array[1..50000]ofboolean; begin fillchar(p,sizeof(p),true); p[1]: =false; i: =2; whilei<50000dobegin ifp[i]thenbegin j: =i*2; whilej<50000dobegin p[j]: =false; inc(j,i); end; end; inc(i); end; l: =0; fori: =1to50000do ifp[i]thenbegin inc(l);pr[l]: =i; end; end;{getprime} functionprime(x: longint): integer; vari: integer; begin prime: =false; fori: =1toldo ifpr[i]>=xthenbreak elseifxmodpr[i]=0thenexit; prime: =true; end;{prime} 二、图论算法 1.最小生成树 算法: procedureprim(v0: integer); var lowcost,closest: array[1..maxn]ofinteger; i,j,k,min: integer; begin fori: =1tondobegin lowcost[i]: =cost[v0,i]; closest[i]: =v0; end; fori: =1ton-1dobegin {寻找离生成树最近的未加入顶点k} min: =maxlongint; forj: =1tondo if(lowcost[j] min: =lowcost[j]; k: =j; end; lowcost[k]: =0;{将顶点k加入生成树} {生成树中增加一条新的边k到closest[k]} {修正各点的lowcost和closest值} forj: =1tondo ifcost[k,j] lowcost[j]: =cost[k,j]; closest[j]: =k; end; end; end;{prim} 算法: (贪心) 按权值递增顺序删去图中的边,若不形成回路则将此边加入最小生成树。 functionfind(v: integer): integer;{返回顶点v所在的集合} vari: integer; begin i: =1; while(i<=n)and(notvinvset[i])doinc(i); ifi<=nthenfind: =ielsefind: =0; end; procedurekruskal; var tot,i,j: integer; begin fori: =1tondovset[i]: =[i];{初始化定义n个集合,第I个集合包含一个元素I} p: =n-1;q: =1;tot: =0;{p为尚待加入的边数,q为边集指针} sort; {对所有边按权值递增排序,存于e[I]中,e[I].v1与e[I].v2为边I所连接的两个顶点的序号,e[I].len为第I条边的长度} whilep>0dobegin i: =find(e[q].v1);j: =find(e[q].v2); ifi<>jthenbegin inc(tot,e[q].len); vset[i]: =vset[i]+vset[j];vset[j]: =[]; dec(p); end; inc(q); end; writeln(tot); end; 2.最短路径 A.标号法求解单源点最短路径: var a: array[1..maxn,1..maxn]ofinteger; b: array[1..maxn]ofinteger;{b[i]指顶点i到源点的最短路径} mark: array[1..maxn]ofboolean; procedurebhf; var best,best_j: integer; begin fillchar(mark,sizeof(mark),false); mark[1]: =true;b[1]: =0;{1为源点} repeat best: =0; fori: =1tondo Ifmark[i]then{对每一个已计算出最短路径的点} forj: =1tondo if(notmark[j])and(a[i,j]>0)then if(best=0)or(b[i]+a[i,j] best: =b[i]+a[i,j];best_j: =j; end; ifbest>0thenbegin b[best_j]: =best;mark[best_j]: =true; end; untilbest=0; end;{bhf} 算法求解所有顶点对之间的最短路径: procedurefloyed; begin forI: =1tondo forj: =1tondo ifa[I,j]>0thenp[I,j]: =Ielsep[I,j]: =0;{p[I,j]表示I到j的最短路径上j的前驱结点} fork: =1tondo{枚举中间结点} fori: =1tondo forj: =1tondo ifa[i,k]+a[j,k] a[i,j]: =a[i,k]+a[k,j]; p[I,j]: =p[k,j]; end; end; C.Dijkstra算法: var a: array[1..maxn,1..maxn]ofinteger; b,pre: array[1..maxn]ofinteger;{pre[i]指最短路径上I的前驱结点} mark: array[1..maxn]ofboolean; proceduredijkstra(v0: integer); begin fillchar(mark,sizeof(mark),false); fori: =1tondobegin d[i]: =a[v0,i]; ifd[i]<>0thenpre[i]: =v0elsepre[i]: =0; end; mark[v0]: =true; repeat{每循环一次加入一个离1集合最近的结点并调整其他结点的参数} min: =maxint;u: =0;{u记录离1集合最近的结点} fori: =1tondo if(notmark[i])and(d[i] u: =i;min: =d[i]; end; ifu<>0thenbegin mark[u]: =true; fori: =1tondo if(notmark[i])and(a[u,i]+d[u] d[i]: =a[u,i]+d[u]; pre[i]: =u; end; end; untilu=0; end; 3.计算图的传递闭包 ProcedureLonglink; Var T: array[1..maxn,1..maxn]ofboolean; Begin Fillchar(t,sizeof(t),false); Fork: =1tondo ForI: =1tondo Forj: =1tondoT[I,j]: =t[I,j]or(t[I,k]andt[k,j]); End; 4.无向图的连通分量 A.深度优先 proceduredfs(now,color: integer); begin fori: =1tondo ifa[now,i]andc[i]=0thenbegin{对结点I染色} c[i]: =color; dfs(I,color); end; end; B宽度优先(种子染色法) 5.关键路径 几个定义: 顶点1为源点,n为汇点。 a.顶点事件最早发生时间Ve[j],Ve[j]=max{Ve[j]+w[I,j]},其中Ve (1)=0; b.顶点事件最晚发生时间Vl[j],Vl[j]=min{Vl[j]–w[I,j]},其中Vl(n)=Ve(n); c.边活动最早开始时间Ee[I],若边I由 d.边活动最晚开始时间El[I],若边I由 若Ee[j]=El[j],则活动j为关键活动,由关键活动组成的路径为关键路径。 求解方法: a.从源点起topsort,判断是否有回路并计算Ve; b.从汇点起topsort,求Vl; c.算Ee和El; 6.拓扑排序 找入度为0的点,删去与其相连的所有边,不断重复这一过程。 例寻找一数列,其中任意连续p项之和为正,任意q项之和为负,若不存在则输出NO. 7.回路问题 Euler回路(DFS) 定义: 经过图的每条边仅一次的回路。 (充要条件: 图连同且无奇点) Hamilton回路 定义: 经过图的每个顶点仅一次的回路。 一笔画 充要条件: 图连通且奇点个数为0个或2个。 9.判断图中是否有负权回路Bellman-ford算法 x[I],y[I],t[I]分别表示第I条边的起点,终点和权。 共n个结点和m条边。 procedurebellman-ford begin forI: =0ton-1dod[I]: =+infinitive; d[0]: =0; forI: =1ton-1do forj: =1tomdo{枚举每一条边} ifd[x[j]]+t[j] =d[x[j]]+t[j]; forI: =1tomdo ifd[x[j]]+t[j] end; 10.第n最短路径问题 *第二最短路径: 每举最短路径上的每条边,每次删除一条,然后求新图的最短路径,取这些路径中最短的一条即为第二最短路径。 *同理,第n最短路径可在求解第n-1最短路径的基础上求解。 三、背包问题 *部分背包问题可有贪心法求解: 计算Pi/Wi 数据结构: w[i]: 第i个背包的重量; p[i]: 第i个背包的价值; 1.0-1背包: 每个背包只能使用一次或有限次(可转化为一次): A.求最多可放入的重量。 NOIP2001装箱问题 有一个箱子容量为v(正整数,o≤v≤20000),同时有n个物品(o≤n≤30),每个物品有一个体积(正整数)。 要求从n个物品中,任取若千个装入箱内,使箱子的剩余空间为最小。 l搜索方法 proceduresearch(k,v: integer);{搜索第k个物品,剩余空间为v} vari,j: integer; begin ifv =v; ifv-(s[n]-s[k-1])>=bestthenexit;{s[n]为前n个物品的重量和} ifk<=nthenbegin ifv>w[k]thensearch(k+1,v-w[k]); search(k+1,v); end; end; lDP F[I,j]为前i个物品中选择若干个放入使其体积正好为j的标志,为布尔型。 实现: 将最优化问题转化为判定性问题 f[I,j]=f[i-1,j-w[i]](w[I]<=j<=v)边界: f[0,0]: =true. ForI: =1tondo Forj: =w[I]tovdoF[I,j]: =f[I-1,j-w[I]]; 优化: 当前状态只与前一阶段状态有关,可降至一维。 F[0]: =true; ForI: =1tondobegin F1: =f; Forj: =w[I]tovdo Iff[j-w[I]]thenf1[j]: =true; F: =f1; End; B.求可以放入的最大价值。 F[I,j]为容量为I时取前j个背包所能获得的最大价值。 F[i,j]=max{f[i–w[j],j-1]+p[j],f[i,j-1]} C.求恰好装满的情况数。 DP: Procedureupdate; varj,k: integer; begin c: =a; forj: =0tondo ifa[j]>0then ifj+now<=ntheninc(c[j+now],a[j]); a: =c; end; 2.可重复背包 A求最多可放入的重量。 F[I,j]为前i个物品中选择若干个放入使其体积正好为j的标志,为布尔型。 状态转移方程为 f[I,j]=f[I-1,j–w[I]*k](k=1..jdivw[I]) B.求可以放入的最大价值。 USACOScoreInflation 进行一次竞赛,总时间T固定,有若干种可选择的题目,每种题目可选入的数量不限,每种题目有一个ti(解答此题所需的时间)和一个si(解答此题所得的分数),现要选择若干题目,使解这些题的总时间在T以内的前提下,所得的总分最大,求最大的得分。 *易想到: f[i,j]=max{f[i-k*w[j],j-1]+k*p[j]}(0<=k<=idivw[j]) 其中f[i,j]表示容量为i时取前j种背包所能达到的最大值。 *实现: Begin FillChar(f,SizeOf(f),0); Fori: =1ToMDo Forj: =1ToNDo Ifi-problem[j].time>=0Then Begin t: =problem[j].point+f[i-problem[j].time]; Ift>f[i]Thenf[i]: =t; End; Writeln(f[M]); End. C.求恰好装满的情况数。 Ahoi2001Problem2 求自然数n本质不同的质数和的表达式的数目。 思路一,生成每个质数的系数的排列,在一一测试,这是通法。 proceduretry(dep: integer); vari,j: integer; begin cal;{此过程计算当前系数的计算结果,now为结果} ifnow>nthenexit;{剪枝} ifdep=l+1thenbegin{生成所有系数} cal; ifnow=ntheninc(tot); exit; end; fori: =0tondivpr[dep]dobegin xs[dep]: =i; try(dep+1); xs[dep]: =0; end; end; 思路二,递归搜索效率较高 proceduretry(dep,rest: integer); vari,j,x: integer; begin if(rest<=0)or(dep=l+1)thenbegin ifrest=0theninc(tot); exit; end; fori: =0torestdivpr[dep]do try(dep+1,rest-pr[dep]*i); end; {main: try(1,n);} 思路三: 可使用动态规划求解 moneysystem V个物品,背包容量为n,求放法总数。 转移方程: Procedureupdate; varj,k: integer; begin c: =a; forj: =0tondo ifa[j]>0then fork: =1tondivnowdo ifj+now*k<=ntheninc(c[j+now*k],a[j]); a: =c; end; {main} begin read(now);{读入第一个物品的重量} i: =0;{a[i]为背包容量为i时的放法总数} whilei<=ndobegin a[i]: =1;inc(i,now);end;{定义第一个物品重的整数倍的重量a值为1,作为初值} fori: =2tovdo begin read(now); update;{动态更新} end; writeln(a[n]); 四、排序算法 A.快速排序: procedureqsort(l,r: integer); vari,j,mid: integer; begin i: =l;j: =r;mid: =a[(l+r)div2];{将当前序列在中间位置的数定义为中间数} repeat whilea[i] whilea[j]>middodec(j);{在右半部分寻找比中间数小的数} ifi<=jthenbegin{若找到一组与排序目标不一致的数对则交换它们} swap(a[i],a[j]); inc(i);dec(j);{继续找} end; untili>j; ifl ifi end;{sort} B.插入排序: 思路: 当前a[1]..a[i-1]已排好序了,现要插入a[i]使a[1]..a[i]有序。 procedureinsert_sort; vari,j: integer; begin fori: =2tondobegin a[0]: =a[i]; j: =i-1; whilea[0] a[j+1]: =a[j]; j: =j-1; end; a[j+1]: =a[0]; end; end;{inset_sort} C.选择排序: proceduresort; vari,j,k: integer; begin fori: =1ton-1do forj: =i+1tondo ifa[i]>a[j]thenswap(a[i],a[j]); end; D.冒泡排序 procedurebubble_sort; vari,j,k: integer; begin fori: =1ton-1do forj: =ndowntoi+1do
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 经典 算法 实例