业精于勤,荒于嬉
全部博文(763)
分类: C/C++
2010-03-01 20:12:02
算法是程序设计的精髓,程序设计的实质就是构造解决问题的算法,将其解释为计算机语言。
算法是在有限步骤内求解某一问题所使用的一组定义明确的规则。通俗点说,就是计算机解题的过程。在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法。前者是推理实现的算法,后者是操作实现的算法。
一个算法应该具有以下五个重要的特征:
动态规划-航线设置
问题描述:美丽的莱茵河畔,每边都分布着N个城市,两边的城市都是唯一对应的友好城市,现需要在友好城市开通航线以加强往来.但因为莱茵河常年大雾,如果开设的航线发生交叉现象就有可能出现碰船的现象.现在要求近可能多地开通航线并且使航线不能相交!
假如你是一个才华横溢的设计师,该如何设置友好城市间的航线使的航线数又最大又不相交呢?
分析:此问题可以演化成求最大不下降序列来完成.源程序如下:
program dongtai; {动态规划之友好城市航线设置问题}
var
d:array[1..1000,1..4] of integer;
i,j,k,n,L,p:integer;
procedure print(L:integer); {打印结果}
begin
writeLn('最多可设置的航线数是 : ',k);
repeat
writeLn(d[L,1]:4,d[L,2]:4); {输出可以设置航线的友好城市代码}
L:=d[L,4]
untiL L=0
end;
begin
writeLn('输入友好城市对数: ');
readLn(n);
writeLn('输入友好城市对(友好城市放在同一行:'); {输入}
for i:=1 to n do
readLn(d[i,1],d[i,2]); {D[I,1]表示起点,D[I,2]表示终点}
for i:=1 to n do
begin
d[i,3]:=1; {D[I,3]表示可以设置的航线条数}
d[i,4]:=0 {D[I,4]表示后继,即下一条航线从哪里开始设置,为0表示不能设置下一条航线}
end;
for i:=n-1 downto 1 do {从倒数第二个城市开始规划}
begin
L:=0; p:=0; {L表示本城市后面可以设置的航线数,P表示下条航线从哪个城市开始}
for j:=i+1 to n do {找出本城市后面可以设置的最大航线数和小条航线到底从哪个城市开始设置}
if (d[i,2]
{如果本城市I的终点小于后面城市的终点(即不相交)} {并且此城市后面可以设置的航线数大于L}
begin
L:=d[j,3]; {那么L等于城市J的可以设置航线数}
p:=j {P等于可以设置下条航线的城市代码}
end;
if L>0 then {如果本城市后面总共可以设置的航线数>0则}
begin
d[i,3]:=L+1; {本城市可以设置的航线数在下个城市可以设置航线数的基础上加1}
d[i,4]:=p {D[I,4]等于本城市后续城市的代码}
end
end;
k:=d[1,3]; {K为可以设置最大航线数,假设初值为第一个城市可以设置的航线数}
L:=1; {L为城市代码,初值为第一个城市}
for i:=2 to n do {找出可以设置航线的最大值,赋值给K,同时L记下哪个可以设置最大航线数的城市代码}
if d[i,3]>k then
begin
k:=d[i,3];
L:=i
end;
for i:=1 to n do {打印结果,因为有可能有多种方案,所以只要哪个城市可以设置的航线数等于最大值K就打印结果}
if d[i,3]=k then print(i)
end.
归并排序算法
合并排序(MERGE SORT)是又一类不同的排序方法,合并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。它的基本思想就是假设数组A有N个元素,那么可以看成数组A是又N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个 N/2 个长度为2或1的有序子序列,再两两合并,如此重复,值得得到一个长度为N的有序数据序列为止,这种排序方法称为2—路合并排序。
例如数组A有7个数据,分别是: 49 38 65 97 76 13 27,那么采用归并排序算法的操作过程如图7所示:
初始值 [49] [38] [65] [97] [76] [13] [27]
看成由长度为1的7个子序列组成
第一次合并之后 [38 49] [65 97] [13 76] [27]
看成由长度为1或2的4个子序列组成
第二次合并之后 [38 49 65 97] [13 27 76]
看成由长度为4或3的2个子序列组成
第三次合并之后 [13 27 38 49 65 76 97]
图6 归并排序算法过程图
合并算法的核心操作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列。合并算法也可以采用递归算法来实现,形式上较为简单,但实用性很差。
合并算法的合并次数是一个非常重要的量,根据计算当数组中有3到4个元素时,合并次数
是2次,当有5到8个元素时,合并次数是3次,当有9到16个元素时,合并次数是4次,按照这一
X
规律,当有N个子序列时可以推断出合并的次数是X(2 >=N,符合此条件的最小那个X)。
源程序:
program hebing;
const
n=10;
var
a:array[1..n] of integer;
i:integer;
procedure init;
var
i:integer;
begin
for i:=1 to n do read(a[i]);
readln;
end;
procedure merge(low,mid,high:integer);
var
h,i,j,k:integer;
b:array[1..n] of integer;
begin
h:=low; i:=low; j:=mid+1;
while (h<=mid) and (j<=high) do
begin
if (a[h]<=a[j]) then
begin
b[i]:=a[h]; h:=h+1;
end
else
begin
b[i]:=a[j]; j:=j+1;
end;
i:=i+1;
end;
if h>mid then
for k:=j to high do
begin
b[i]:=a[k]; i:=i+1;
end
else
for k:=h to mid do
begin
b[i]:=a[k]; i:=i+1;
end;
for k:=low to high do
a[k]:=b[k];
end;
procedure mergesort(low,high:integer);
var
mid:integer;
begin
if low
mid:=(low+high) div 2;
mergesort(low,mid);
mergesort(mid+1,high);
merge(low,mid,high);
end;
end;
枚举法
有4个学生,上地理课时提出我国四大谈水湖的排列次序如下:
甲:洞庭湖最大,洪泽湖最小,鄱阳湖第三;
乙:洪泽湖最大,洞庭湖最小,鄱阳湖第二,太湖第三;
丙:洪泽湖最小,洞庭湖第三;
丁:鄱阳湖最大,太湖最小,洪泽湖第二,洞庭湖第三;
对于各湖泊应处的位置,每个人只说对了一个。根据以上描述和条件,编写程序,让计算机判断一下各湖泊应该处于第几位。
解题思路:设置洞庭湖、洪泽湖、鄱阳湖、太湖分别用字母A、B、C、D代表,最大到最小依次用数字4——1表示。应用枚举法可以解决此问题,不过稍微复杂罗嗦点。
源程序如下:
program hupo;
var
a,b,c,d:integer;
begin
for b:=1 to 4 do
for a:=1 to 4 do
if ((b=1) and (a<>2)) or ((a=2) and (b<>1)) then
if a<>b then
for c:=1 to 4 do
if (a<>c) and (b<>c) then
if (a+b+c<>7) and (a+b<>5) and (a+c<>6) and (b+c<>3) then
for d:=1 to 4 do
if c<>d then
if (b+a<>5) and (b+c<>7) and (b+d<>6) then
if (a+c<>4) and (a+d<>3) and (c+d<>5) then
if (c+d<>5) and (c+b<>7) and (c+a<>6) then
if (d+b<>4) and (d+a<>3) and (b+a<>5) then
writeln('a=',a,' b=',b,' c=',c,' d=',d)
end.
改进程序:
program ygzj;
var
a,b,c,d:integer;
begin
for a:=1 to 4 do
for b:=1 to 4 do
for c:=1 to 4 do
begin
d:=10-a-b-c;
if ord(a=1)+ord(b=4)+ord(c=3)=1 then
if ord(b=1)+ord(a=4)+ord(c=2)+ord(d=3)=1 then
if ord(b=4)+ord(a=3)=1 then
if ord(c=1)+ord(d=4)+ord(b=2)+ord(a=3)=1 then
if a*b*c*d=24 then
writeln('洞庭湖第':3,a:3,'洪泽湖第':3,b:3,'波阳湖第':3,c:3,'太湖第':3,d:3);
end;
writeln
end.
数字全排列问题:
任意给出从1到N的N个连续的自然数,求出这N个自然数的各种全排列。如N=3时,共有以下6种排列方式:
123,132,213,231,312,321。
注意:数字不能重复,N由键盘输入(N<=9)。
解题思路:
应用回溯法,每个数的取法都有N个方向(1——N),当取够N个数时,输出一个排列,然后退后一步,取前一个数的下一个方向(即前一个数+1),并且要保证所有数字不能重复。当前数字的所有方向都取完时,继续退一步,一直重复到第一个数为止。
程序代码:
program quanpailie; {数字全排列问题}
var
a:array[1..9] of integer;
k,x,n:integer;
function panduan(j,h:integer):boolean; {判断当前数字是否能赋值给当前数组元素}
var
m:integer;
begin
panduan:=true;
for m:=1 to h-1 do
if a[m]=j then panduan:=false {如果当前数字与前面的数组元素相同则不能赋值}
end;
procedure try(h:integer);
var
i,j,m:integer;
begin
for j:=1 to n do
if panduan(j,h) then
begin
a[h]:=j; {能够赋值,且长度k加一}
k:=k+1;
if k=n then {如果长度达到N则表示一种组合已经完成,输出结果}
begin
for m:=1 to n do
write(a[m]);
write('':4);
x:=x+1; {每输出一种排列方式加一}
if x mod 5=0 then writeln; {每行输出5种排列方案}
end
else
try(h+1); {对下一个数组元素进行赋值}
k:=k-1 {回溯的时候一定要把长度减一}
end
end;
begin
writeln('输入 N:');
readln(n);
k:=0; {k表示长度,长度初始值为0}
x:=0; {x表示总的排列方式}
try(1); {对第一个数组元素赋值}
writeln('共有 ', x ,' 种排列方案')
end.