算法大全(
C,C++)
一、数论算法
1.求两数的最大公约数
function
gcd(a,b:integer):integer;
begin
if
b=0
then
gcd:=a
else
gcd:=gcd
(b,a
mod
b);
end
;
2.求两数的最小公倍数
function
lcm(a,b:integer):integer;
begin
if
a<b
then
swap(a,b);
lcm:=a;
while
lcm
mod
b>0
do
inc(lcm,a);
end;
3.素数的求法
A.小范围内判断一个数是否为质数:
function
prime
(n:integer):
Boolean;
var
I:
integer;
begin
for
I:=2
to
trunc(sqrt(n))
do
if
n
mod
I=0
then
begin
prime:=false;
exit;
end;
prime:=true;
end;
B.判断
longint范围内的数是否为素数(包含求
50000以内的素数表):
procedure
getprime;
var
i,j:longint;
p:array[1..50000]
of
boolean;
begin
fillchar(p,sizeof(p),true);
p[1]:=false;
i:=2;
while
i<50000
do
begin
if
p[i]
then
begin
j:=i*2;
while
j<50000
do
begin
p[j]:=false;
inc(j,i);
end;
end;
inc(i);
end;
l:=0;
for
i:=1
to
50000
do
if
p[i]
then
begin
inc(l);pr[l]:=i;
end;
end;{getprime}
function
prime(x:longint):integer;
var
i:integer;
begin
prime:=false;
for
i:=1
to
ldo
if
pr[i]>=x
then
break
else
if
x
mod
pr[i]=0
then
exit;
prime:=true;
end;{prime}
二、图论算法
1.最小生成树
A.Prim算法:
procedure
prim(v0:integer);
var
lowcost,closest:axn]
of
integer;
i,j,k,min:integer;
begin
for
i:=1
to
n
do
begin
lowcost[i]:=cost[v0,i];
closest[i]:=v0;
end;
for
i:=1
to
n-1
do
begin
{寻离生成树最近的未加入顶点
k}
min:=maxlongint;
for
j:=1
to
n
do
if
(lowcost[j]<min)
and
(lowcost[j]<>0)
then
begin
min:=lowcost[j];
k:=j;
end;
lowcost[k]:=0;
{将顶点
k加入生成树
}
{生成树中增加一条新的边
k到
closest[k]}
{修正各点的
lowcost和
closest值}
for
j:=1
to
n
do
if
cost[k,j]<lwocost[j]
then
begin
lowcost[j]:=cost[k,j];
closest[j]:=k;
end;
end;
end;{prim}
B.Kruskal算法:(贪心)
按权值递增顺序删去图中的边,若不形成回路则将此边加入最小生成树。
function
find(v:integer):integer;
{返回顶点
v所在的集合
}
var
i:integer;
begin
i:=1;
while
(i<=n)
and
(not
v
in
vset[i])
do
inc(i);
if
i<=n
then
find:=i
else
find:=0;
end;
procedure
kruskal;
var
tot,i,j:integer;
begin
for
i:=1
to
n
do
vset[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条边的长度
}
while
p
>0
do
begin
i:=find(e[q].v1);j:=find(e[q].v2);
if
i<>jthen
begin
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:axn]
of
integer;
b:axn]
of
integer;
{b[i]指顶点
i到源点的最短路径
}
mark:axn]
of
boolean;
procedure
bhf;
var
best,best_j:integer;
begin
fillchar(mark,sizeof(mark),false);
mark[1]:=true;
b[1]:=0;{1为源点}
repeat
best:=0;
for
i:=1
to
n
do
If
mark[i]
then
{对每一个已计算出最短路径的点
}
for
j:=1
to
n
do
if
(not
mark[j])
and
(a[i,j]>0)
then
if
(best=0)
or
(b[i]+a[i,j]<best)
then
begin
best:=b[i]+a[i,j];
best_j:=j;
end;
if
best>0
then
begin
b[best_j]:=best;mark[best_j]:=true;
end;
until
best=0;
end;{bhf}
B.Floyed算法求解所有顶点对之间的最短路径:
procedure
floyed;
begin
for
I:=1
to
n
do
for
j:=1
to
n
do
if
a[I,j]>0
then
p[I,j]:=I
else
p[I,j]:=0;
{p[I,j]表示
I到
j的最短路径上
j的前驱结点
}
for
k:=1
to
n
do
{枚举中间结点
}
for
i:=1
to
n
do
for
j:=1
to
n
do
if
a[i,k]+a[j,k]<a[i,j]
then
begin
a[i,j]:=a[i,k]+a[k,j];
p[I,j]:=p[k,j];
end;
end;
C.
Dijkstra算法:
var
a:axn]
of
integer;
b,pre:axn]
of
integer;
{pre[i]指最短路径上
I的前驱结点
}
mark:axn]
of
boolean;
procedure
dijkstra(v0:integer);
begin
fillchar(mark,sizeof(mark),false);
for
i:=1
to
n
do
begin
d[i]:=a[v0,i];
if
d[i]<>0
then
pre[i]:=v0
else
pre[i]:=0;
end;
mark[v0]:=true;
repeat{每循环一次加入一个离
1集合最近的结点并调整其他结点的参数
}
min:=maxint;
u:=0;
{u记录离
1集合最近的结点
}
for
i:=1
to
n
do
if
(not
mark[i])
and
(d[i]<min)
then
begin
u:=i;
min:=d[i];
end;
if
u<>0
then
begin
mark[u]:=true;
for
i:=1
to
n
do
if
(not
mark[i])
and
(a[u,i]+d[u]<d[i])
then
begin
d[i]:=a[u,i]+d[u];
pre[i]:=u;
end;
end;
until
u=0;
end;
3.计算图的传递闭包
Procedure
Longlink;
Var
T:axn]
of
boolean;
Begin
Fillchar(t,sizeof(t),false);
For
k:=1
to
n
do
For
I:=1
to
n
do
For
j:=1
to
n
do
T[I,j]:=t[I,j]
or
(t[I,k]
and
t[k,j]);
End;
4.无向图的连通分量
A.深度优先
procedure
dfs
(
now,color:
integer);
begin
for
i:=1
to
n
do
if
a[now,i]
and
c[i]=0
then
begin
{对结点
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由<j,k>表示,则
Ee[I]
=
Ve[j];
d.边活动最晚开始时间
El[I],若边
I由<j,k>表示,则
El[I]
=
Vl[k]
–
w[j,k];
若
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条边。
procedure
bellman-ford
begin
for
I:=0
to
n-1
do
d[I]:=+infinitive;
d[0]:=0;
for
I:=1
to
n-1
do
for
j:=1
to
m
do
{枚举每一条边
}
if
d[x[j]]+t[j]<d[y[j]]
then
d[y[j]]:=d[x[j]]+t[j];
for
I:=1
to
m
do
if
d[x[j]]+t[j]<d[y[j]]
then
return
false
else
return
true;
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搜索方法
procedure
search(k,v:integer);
{搜索第
k个物品,剩余空间为
v}
var
i,j:integer;
begin
if
v<best
then
best:=v;
if
v-(s[n]-s[k-1])>=best
then
exit;
{s[n]为前
n个物品的重量和
}
if
k<=n
then
begin
if
v>w[k]
then
search(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.
For
I:=1
to
n
do
For
j:=w[I]
to
v
do
F[I,j]:=f[I-1,j-w[I]];
优化:当前状态只与前一阶段状态有关,可降至一维。
F[0]:=true;
For
I:=1
to
n
do
begin
F1:=f;
For
j:=w[I]
to
v
do
If
f[j-w[I]]
then
f1[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:
Procedure
update;
var
j,k:integer;
begin
c:=a;
for
j:=0
to
n
do
if
a[j]>0
then
if
j+now<=n
then
inc(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..
jdiv
w[I])
B.求可以放入的最大价值。
USACO
1.2
Score
Inflation
进行一次竞赛,总时间
T固定,有若干种可选择的题目,每种题目可选入的数量不限,每
种题目有一个
ti(解答此题所需的时间)和一个
si(解答此题所得的分数),现要选择若干
题目,使解这些题的总时间在
T以内的前提下,所得的总分最大,求最大的得分。
*易想到:
f[i,j]
=
max
{
f
[i-k*w[j],
j-1]
+
k*p[j]
}
(0<=k<=
idiv
w[j])
其中
f[i,j]表示容量为
i时取前
j种背包所能达到的最大值。
*实现:
Begin
FillChar(f,SizeOf(f),0);
For
i:=1
ToM
Do
For
j:=1
ToN
Do
If
i-problem[j].time>=0
Then
Begin
t:=problem[j].point+f[i-problem[j].time];
If
t>f[i]
Then
f[i]:=t;
End;
Writeln(f[M]);
End.
C.求恰好装满的情况数。
Ahoi2001
Problem2
求自然数
n本质不同的质数和的表达式的数目。
思路一,生成每个质数的系数的排列,在一一测试,这是通法。
procedure
try(dep:integer);
var
i,j:integer;
begin
cal;
{此过程计算当前系数的计算结果,
now为结果}
if
now>n
then
exit;
{剪枝}
if
dep=l+1
then
begin
{生成所有系数
}
cal;
if
now=n
then
inc(tot);
exit;
end;
for
i:=0
to
n
div
pr[dep]
do
begin
xs[dep]:=i;
try(dep+1);
xs[dep]:=0;
end;
end;
思路二,递归搜索效率较高
procedure
try(dep,rest:integer);
var
i,j,x:integer;
begin
if
(rest<=0)
or
(dep=l+1)
then
begin
if
rest=0
then
inc(tot);
exit;
end;
for
i:=0
to
rest
div
pr[dep]
do
try(dep+1,rest-pr[dep]*i);
end;
{main:
try(1,n);}
思路三:可使用动态规划求解
U
SACO1.2
money
system
V个物品,背包容量为
n,求放法总数。
转移方程:
Procedure
update;
var
j,k:integer;
begin
c:=a;
for
j:=0
to
n
do
if
a[j]>0
then
for
k:=1
to
n
div
now
do
if
j+now*k<=n
then
inc(c[j+now*k],a[j]);
a:=c;
end;
{main}
begin
read(now);{读入第一个物品的重量
}
i:=0;
{a[i]为背包容量为
i时的放法总数
}
while
i<=n
do
begin
a[i]:=1;
inc(i,now);
end;{定义第一个物品重的整数倍的重量
a值为
1,作为初值
}
for
i:=2
to
v
do
begin
read(now);
update;
{动态更新}
end;
writeln(a[n]);
四、排序算法
A.快速排序:
procedure
qsort(l,r:integer);
var
i,j,mid:integer;
begin
i:=l;j:=r;
mid:=a[(l+r)
div
2];{将当前序列在中间位置的数定义为中间数
}
repeat
while
a[i]<mid
do
inc(i);
{在左半部分寻比中间数大的数
}
while
a[j]>mid
do
dec(j);{在右半部分寻比中间数小的数
}
if
i<=jthen
begin
{若到一组与排序目标不一致的数对则交换它们
}
swap(a[i],a[j]); c语言算法书籍
inc(i);dec(j);
{继续}
end;
until
i>j;
if
l<j
then
qsort(l,j);{若未到两个数的边界,则递归搜索左右区间
}
if
i<r
then
qsort(i,r);
end;{sort}
B.插入排序:
思路:当前
a[1]..a[i-1]已排好序了,现要插入
a[i]使
a[1]..a[i]有序。
procedure
insert_sort;
var
i,j:integer;
begin
for
i:=2
to
n
do
begin
a[0]:=a[i];
j:=i-1;
while
a[0]<a[j]
do
begin
a[j+1]:=a[j];
j:=j-1;
end;
a[j+1]:=a[0];
end;
end;{inset_sort}
C.选择排序:
procedure
sort;
var
i,j,k:integer;
begin
for
i:=1
to
n-1
do
for
j:=i+1
to
n
do
if
a[i]>a[j]
then
swap(a[i],a[j]);
end;
D.冒泡排序
procedure
bubble_sort;
var
i,j,k:integer;
begin
for
i:=1
to
n-1
do
for
j:=n
downto
i+1
do
if
a[j]<a[j-1]
then
swap(
a[j],a[j-1]);
{每次比较相邻元素的关系
}
end;
E.堆排序:
procedure
sift(i,m:integer);{调整以
i为根的子树成为堆
,m为结点总数
}
var
k:integer;
begin
a[0]:=a[i];
k:=2*i;{在完全二叉树中结点
i的左孩子为
2*i,右孩子为
2*i+1}
while
k<=m
do
begin
if
(k<m)
and
(a[k]<a[k+1])
then
inc(k);{出
a[k]与
a[k+1]中较大值}
if
a[0]<a[k]
then
begin
a[i]:=a[k];i:=k;k:=2*i;
end
else
k:=m+1;
end;
a[i]:=a[0];
{将根放在合适的位置
}
end;
procedure
heapsort;
var
j:integer;
begin
for
j:=n
div
2
downto
1
do
sift(j,n);
for
j:=n
downto
2do
begin
swap(a[1],a[j]);
sift(1,j-1);
end;
end;
F.归并排序
{a为序列表,
tmp为辅助数组
}
procedure
merge(var
a:listtype;
p,q,r:integer);
{将已排序好的子序列
a[p..q]与
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论