then
begin
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]
of
integer;
b,pre:
array[1..maxn]
of
integer;
{pre[i]指最短路径上
I的前驱结点
}
mark:
array[1..maxn]
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]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]then
begin
d[i]:
=a[u,i]+d[u];
pre[i]:
=u;
end;
end;
until
u=0;
end;
3.计算图的传递闭包
Procedure
Longlink;
Var
T:
array[1..maxn,1..maxn]
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由表示,则
Ee[I]
=
Ve[j];
d.边活动最晚开始时间
El[I],若边
I由表示,则
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]then
d[y[j]]:
=d[x[j]]+t[j];
for
I:
=1
to
m
do
if
d[x[j]]+t[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
vthen
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);}
思路三:
可使用动态规划求解
USACO1.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]do
inc(i);
{在左半部分寻找比中间数大的数
}
while
a[j]>mid
do
dec(j);{在右半部分寻找比中间数小的数
}
if
i<=jthen
begin
{若找到一组与排序目标不一致的数对则交换它们
}
swap(a[i],a[j]);
inc(i);dec(j);
{继续找}
end;
until
i>j;
if
lthen
qsort(l,j);{若未到两个数的边界,则递归搜索左右区间
}
if
ithen
qsor