计算机论文.docx
《计算机论文.docx》由会员分享,可在线阅读,更多相关《计算机论文.docx(22页珍藏版)》请在冰豆网上搜索。
计算机论文
毕业论文
题目:
NOI算法分析与例题解析
学院:
数学与信息科学学院
专业:
计算机科学与技术(师)
毕业年限:
2011年6月
学生姓名:
曹黎斌
学号:
200771030101
指导教师:
王立群
目录
1引言1
1.1动态规划法1
1.2排列与组合1
2算法解析及程序实现1
2.1数字三角形1
2.2最长公共子序列1
2.3球迷购票问题1
参考文献1
致谢1
NOI算法分析与例题解析
曹黎斌
(西北师范大学数学与信息科学学院计算机科学与技术(师),甘肃兰州730070)
中文摘要:
论文是选取了全国青少年信息学奥林匹克联赛里面的题目,对题目的算法进行了详细的分析,并对里面的一些例题的进行了详细的解析。
选取的题目都是关于动态规划的问题,即:
对于一个具体的大问题,我们总是想方设法把它分成两个或多个更小的问题,然后分别解决每个小问题;再把各个小问题的解组合起来,即可得到原问题的解答。
所选的题目包括数字三角形、最长公共子序列和花店橱窗布置三道题目。
以上的题目都是由Pascal语言编写的,但现在c语言的学习和使用比较多,所以本论文的主要任务是将用Pascal语言写的以上题目的程序用c语言写出来,并能够正常的运行,能达到与Pascal语言一样的运行效果。
关键词:
动态规划,Pascal程序,c程序
NOIAlgorithmAnalysisandExamplesResolution
Abstract:
PaperistoselectanationalyouthleagueOlympiadinInformatics,thesubjectcarriedoutadetailedanalysisofalgorithms,andthereweresomeexamplesofdetailedanalysis.Topicsareselectedonthedynamicprogrammingproblem,namely:
thebigissueforaspecific,wearealwaystryingtoputitintotwoormoresmallerproblems,thensolveeachsmallproblem,respectively;theneverysmallproblemcombinedsolution,youcangetanswerstotheoriginalproblem.Theselectedtopicsincludedigitaltriangle,thelongestcommonsubsequenceandflowershopwindowdisplayofthreetopics.TheabovequestionsarewrittenbythePascallanguage,butnowclanguagelearningandusemore,sothemaintaskofthispaperistousethePascallanguageprogramswrittenintheabovesubjectwrittenbyclanguage,andthenormaloperation,canachievethesamewiththePascallanguageoperatingresults.
Keywords:
dynamicprogramming,Pascalprogram,cprogram
1引言
教育部和中国科协委托中国计算机学会举办了全国青少年计算机程序设计竞赛(简称:
NOI),旨在向那些在中学阶段学习的青少年普及计算机科学知识;给学校的信息技术教育课程提供动力和新的思路;给那些有才华的学生提供相互交流和学习的机会;通过竞赛和相关的活动培养和选拔优秀计算机人才。
1984年参加竞赛的有8000多人。
这一新的活动形式受到党和政府的关怀,得到社会各界的关注与支持。
中央领导王震同志出席了首届竞赛发奖大会,并对此项活动给予了充分肯定。
从此每年一次NOI活动,吸引越来越多的青少年投身其中。
为了在更高层次上推动普及,培养更多的计算机技术优秀人才。
竞赛及相关活动遵循开放性原则,任何有条件和兴趣的学校和个人,都可以在业余时间自愿参加。
本人亦对此对于其中的几种算法进行了分析并读了NOI相关的教程及C语言、Pascal语言的程序设计和教程,并选出几道有代表性的题目,将原教程上用Pascal语言实现的程序进行了改写或重写,并全部改用C语言编程实现.
1.1动态规划法
动态规划问世以来,在经济管理、生产调度、工程技术和最优控制等方面得到了广泛的应用。
例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其它方法求解更为方便。
虽然动态规划主要用于求解以时间划分阶段的动态过程的优化问题,但是一些与时间无关的静态规划(如线性规划、非线性规划),只要人为地引进时间因素,把它视为多阶段决策过程,也可以用动态规划方法方便地求解。
动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。
不象前面所述的那些搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。
动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。
因此本人在学习时,不仅对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。
动态规划算法通常用于求解具有某种最优性质的问题。
在这类问题中,可能会有许多可行解。
每一个解都对应于一个值,我们希望找到具有最优值的解。
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。
如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。
1.2排列与组合
排列组合是组合学最基本的概念。
所谓排列,就是指从给定个数的元素中取出指定个数的元素进行排序。
组合则是指从给定个数的元素中仅仅取出指定个数的元素,不考虑排序。
排列组合的中心问题是研究给定要求的排列和组合可能出现的情况总数。
2算法解析及程序实现
2.1数字三角形
【问题描述】
7
38
810
2744
45265
上图给出了一个数字三角形。
从三角形的顶部到底部有很多条不同的路径。
对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。
你的任务就是求出最佳路径上的数字之和。
注意:
路径上的每一步只能从一个数走到下一层上和它最近的左边的数或者右边的数。
输入:
输入的第一行是一个整数N(1下面的N行给出数字三角形。
数字三角形上的数的范围都在0和100之间。
输出:
输出为单独的一行,这一行中是一个整数,该整数为数字三角形的最大和。
【输入样例】
5
7
38
810
2744
45265
【输出样例】
30
【问题分析】
假设从顶至数字三角形的某一位置的所有路径中,所经过的数字总和最大的那条路径我们称之为该位置的最大路径。
由于问题规定每一步只能沿直线或右斜线向下走,若要走到某一位置,则其前一位置比为其左上方或正上方两个位置之一。
由此可知,当前位置的最优路径必定与其左上方或正上方两个位置的最优路径有关,且来自与其中更优的一个。
我们可以用一个两位数组a记录数字三角形中的数,a[i,j]表示数字三角形中第i行第j列的数,再用一个两维数组maxsum记录每个位置的最优路径的数字总和。
计算maxsum数组可以像计算杨辉三角形一样用逐行递推的方法实现,具体细节请看程序清单。
【pascal源代码】
Programp8_1(input,output);
constmaxline=100;
vari,j,t,line:
integer;
a,max:
array[1..maxline,1..maxline]ofinteger;
functionmaxsum(i,j:
integer):
integer;
begin
ifi=line
thenmaxsum:
=a[i,j]
elseifmaxsum(i+1,j)>maxsum(i+1,j)+a[i,j]
elsemaxsum:
=maxsum(i+1,j+1)+a[i,j]
end;
begin{main}
write('inputnumberofline(<=100):
');readln(line);
fori:
=1tolinedo
begin
forj:
=1toido
begin
a[i,j]:
=random(99)+1;
write(a[i,j]:
3)
writeln
end;
writeln;
writeln('maxsum=',maxsum(1,1));
max[1,1]:
=a[1,1];
fori:
=2tolinedo
begin
max[i,1]:
=max[i-1,1]+a[1,1];
forj:
=2toi-1do
ifa[i,j]+max[i-1,j]>=a[i,j]+max[i-1,j-1]
thenmax[i,j]:
=a[i,j]+max[i-1,j]
elsemax[i,j]:
=a[i,j]+max[i-1,j-1];
max[i,j]:
=max[i-1,i-1]+a[i,i]
end;
t:
=0;
fori:
=1tolinedo
ift=max[line,i];
writeln('Thenmaximunsumis:
',t)
end.
【C程序代码】
#include
#defineMAX_NUM110
intN,aMaxSum[MAX_NUM][MAX_NUM],D[MAX_NUM][MAX_NUM];
intMaxSum(inti,intj){
if(i==N)
returnD[i][j];
if(aMaxSum[i+1][j]==-1)
aMaxSum[i+1][j]=MaxSum(i+1,j);
if(aMaxSum[i+1][j+1]==-1)
aMaxSum[i+1][j+1]=MaxSum(i+1,j+1);
if(aMaxSum[i+1][j]>aMaxSum[i+1][j+1])
returnaMaxSum[i+1][j]+D[i][j];
returnaMaxSum[i+1][j+1]+D[i][j];
}
intmain()
{
inti,j;
scanf("%d",&N);
for(i=1;i<=N;i++)
for(j=1;j<=i;j++)
aMaxSum[i][j]=-1;
for(i=1;i<=N;i++)
for(j=1;j<=i;j++)
scanf("%d",&D[i][j]);
printf("%d\n",MaxSum(1,1));
return0;
}
【运行结果】
2.2最长公共子序列
【问题描述】
一个给定的序列的子序列是该序列中删除去若干元素后得到的序列。
确切的説,若给定序列X=,则另一序列Z=是x的子序列是指存在一个严格的递增的下标序列使得对j=1,2,...,k,有xij=zj。
比如Z=是X=的子序列。
现在给出两个序列X和Y,你的任务是找到X和Y的最大公共子序列,也就是说要找到一个最长的序列Z,使得Z既是X的子序列也是Y的子序列。
例如,若x=和y=,则序列是x和y的一个公共子序列,序列也是x和y的一个公共子序列。
而且,后者是x和y的一个最长公共子序列,因为x和y没有长度大于4的公共子序列。
给定两个序列X=和Y=,要求找出x和y的一个最长公共子序列。
输入:
输入文件共有两行,每行为一个由大写字母构成的长度不超过200的字符串,表示序列x和y。
输出:
输出文件第一行为一个非负整数,表示所求得的最长公共子序列的长度,若不存在公共子序列,则输出文件仅有一行输出一个整数0,否则在输出文件的第二行输出所求得的最长公共子序列(一用一个大写字母组成的字符串表示),若符合条件的最长公共子序列不止一个,只需输出其中任意一个。
【样例输入】LCS.IN
ABCBDAB
BDCABA
【样例输出】LCS.OUT
BCBA
【问题分析】
动态规划算法可有效的解决此问题,下面我们按照动态规划算法设计的各个步骤来设计一个解决此问题的有效算法。
最长公共子序列的结构
解最长公共子序列问题时最容易想到的算法是穷举搜索法,即对么一个子序列,检查它是否也是y的子序列,从而确定他是否为x和y的公共子序列。
并且在检查过程中选出最长的公共子序列。
X的所有子序列都检查过后即可求出x和y的最长公共子序列。
X的一个子序列相应于下标序列{1,2,…,m}的一个子序列,因此,x共有2m个不同子序列,从而穷举搜索法需要指数时间。
事实上,最长公共子序列问题也有最优子结构性质,因为我们有如下定理:
定理:
LCS的最优子结构性质
设序列X=和Y=的一个最长公共子序列Z=,则:
若Xm=Yn,则Zk=Xm=Yn且Zk-1是Xm-1和Yn-1的最长公共子序列:
若Xm!
=Yn,则Zk!
=Xm,则Z是Xm-1和Y的最长公共子序列:
若Xm!
=Yn且Zk!
=Yn,则Z是X和Yn-1的最长公共子序列。
证明:
用反证法。
若Zk !
=Xm,则是X和Y的长度为k+1的公共子序列。
这与z是x和y的一个最长公共子序列相矛盾。
因此,必有Zk=Xm=Yn。
由此可知Zk-1是Xm-1和Yn-1的一个长度为k-1的公共子序列。
若Xm-1和Yn-1有一个长度大于k-1的公共序列w,则将Xm加在其尾部将产生x和y的一个长度大于k的公共子序列,此为矛盾。
故Zk-1是Xm-1和Yn-1的一个最长公共子序列。
又由于Zk!
=Xm,Z是Xm-1和Y的一个公共子序列。
若Xm-1和Y有一个长度大k的公共子序列w,则w也是X和Y的一个长度大于k的公共子序列。
这与Z是X和Y的一个最长公共子序列矛盾,由此即知Z是Xm-1和Y的一个最长公共子序列。
这个定理告诉我们,两个序列的最长公共子序列包含了这两个序列的前缀的最长公共子序列。
因此最长公共子序列问题具有最优子结构性质。
2.子问题重叠性质
有最长公共子序列问题的最优子结构性质可知,要找出X=和Y=的最长公共子序列,可按照以下方式递归的进行:
当Xm=Yn时,找出Xm-1和Yn-1的最长公共子序列,然后在其尾部加上Xm(=Yn)即可得X和Y的一个最长公共子序列。
当Xm!
=Yn时,必须解两个子问题,即找出Xm-1和Y的一个最长公共子序列即X和Yn-1的一个最长公共子序列。
这两个公共子序列中较长者即为X和Y的一个最长公共子序列。
由此递归结构容易看到最长公共子序列问题具有子问题重叠性质。
例如,在计算X和Y的最长公共那个子序列时,可能要计算X和Yn-1及Xm-1和Y的最长公共子序列。
而这两个子问题都含一个公共子问题,即计算Xm-1和Yn-1的最长公共子序列。
我们来建立子问题的最优值的递归关系。
用c[i,j]记录序列Xi和Yj的最长公共子序列的长度。
其中X=,Y=。
当i=0或j=0时,空序列是xi和Yj的最长公共子序列,故c[i,j]=0。
其他情况下,有定理可建立递归关系如下。
0当i=0或j=0时
C[i,j]=c[i-1,j-1]+1当i,j>0且Xi=Yj时
max(c[I,j-1],c[i-1,j])当i,j>0且Xi!
=Yj时
3.计算最优值
直接利用上式容易写出一个计算c[i,j]的递归算法,但其计算时间是随输入长度指数增长的。
由于在所考虑的子问题空间中,总共有O(m*n)个不同的子问题,因此,用动态规划算法自底向上的计算最优值能提高算法的效率。
计算最长公共子序列长度的动态规划算法LCS_LENGTH(X,Y)以序列X=和Y=作为输入。
输出两个数组c[0..m,0..n]和b[2..m,2..n]。
其中c[i,j]存储Xi和Yj的最长公共子序列的长度,b[i,j]记录指示c[i,j]的值是有哪一个子问题的解达到的,这在构造最长公共子序列是要用到。
最后,x和y的最长公共子序列的长度记录与c[m,n]中。
由于每个数组单元的计算耗费o
(1)时间,算法LCS_LENGTH耗时O(MN)。
4.构造最长公共子序列
由算法LCS_LENGTH计算得到的数组b可用于快速构造序列X=和Y=的最长公共子序列。
首先从b[m,n]开始,沿着其中的箭头所指的方向在数组b中搜索。
当b[I,j]中遇到斜箭头时,表示Xi与Yj的最长公共子序列是由Xi-1与Yj-1的最长公共子序列在尾部加上Xi得到的子序列;当b[I,j]中遇到向↑时表示Xi与Yj的最长公共子序列和Xi-1与Yj的最长公共子序列相同;当当b[I,j]中遇到向←时表示Xi与Yj的最长公共子序列和Xi与Yj-1的最长公共子序列相同;
【pascal源程序】
programp8_2(input,output);
constmaxlen=200;
vari,j:
longint;
c:
array[0..maxlen,0..maxlen]ofbyte;
x,y,z:
string;
begin
assign(input,'lcs.in');
reset(input);
readln(x);
readln(y);
close(input);
fillchar(c,sizeof(c),0);
fori:
=1tolength(x)do
forj:
=1tolength(y)do
ifx[i]=y[j]
thenc[i,j]:
=c[i-1,j-1]+1
elseifc[i-1,j]>c[i,j-1]
thenc[i,j]:
=c[i-1,j]
elsec[i,j]:
=c[i,j-1];
z:
='';
i:
=length(x);
j:
=length(y);
assign(output,'lcs.out');
rewrite(output);
writeln(c[i,j]);
while(i>0)and(j>0)do
ifx[i]=y[j]
thenbeginz:
=x[i]+z;
i:
=i-1;
j:
=j-1;
end
elseifc[i-1,j]>c[i,j-1]
theni:
=i-1
elsej:
=j-1;
ifz<>''thenwriteln(z);
close(output)
end.
【c程序】
#include
#include
#defineMAX_LEN200
intmain()
{
chars1[MAX_LEN+10],s2[MAX_LEN+10];
intMaxLen[MAX_LEN+10][MAX_LEN+10];
inti,j,len1,len2,len3,len4;
while(scanf("%s%s",s1+1,s2+1)>0){
len1=strlen(s1+1);
len2=strlen(s2+1);
for(j=0;j<=len1;j++)
MaxLen[0][j]=0;
for(i=0;i<=len2;i++)
MaxLen[i][0]=0;
for(i=1;i<=len1;i++){
for(j=1;j<=len2;j++){
if(s1[i]==s2[j])
MaxLen[i][j]=MaxLen[i-1][j-1]+1;
else{
len3=MaxLen[i-1][j];
len4=MaxLen[i][j-1];
if(len3>len4)
MaxLen[i][j]=len3;
else
MaxLen[i][j]=len4;
}
}
}
printf("%d\n",MaxLen[len1][len2]);
}
return0;}
【运行结果】
2.3球迷购票问题
【问题描述】
盛况空前的足球赛即将举行。
球赛门票售票处排起了长龙。
按售票处规定每位购票者限购一张门票,且每张售票价为50元。
在排成长龙的球迷中,有m个人手持面值50元的钱币,另有n个人手持面值100元的钱币。
假设售票处在开始售票时没有零钱。
试问这m+n个球迷有多少种排队方式可使售票处不致出现找不出钱的尴尬局面。
例如当m=3,n=2时,用A表示手持面值50元钱币的球迷,用B表示手持面值100元钱币的球迷,则最多可以得到以下5组不同的排队方式,使售票处不致出现找不出钱的尴尬局面。
售票处
A
A
A
B
B
售票处
A
A
B
A
B
售票处
A
B
A
A
B
售票处
A
A
B
B
A
售票处
A
B
A
B
A
编程任务:
对于给定的m和n的值(0≤m,n≤5000),编程计算出m+n个球迷有多少种排队方式可使售票处不致出现找不出钱的尴尬局面。
数据输入:
【输入】
football.in,共一行表示m和n的值。
【输出】
程序运行结束时,将计算出的排队方式数写入文件名为football.out的文本文件中。
每个计算结果占两行,第一行为输出数据的十进制位数,第二行是相应的输出数据。
【样例】
football.in
2
football.out
1
5
【问题分析】
假设将手持50元人民币的球迷记为S,持100元人民币的球迷则记为X,则m个手持50元人民币与n个手持100元人民币的球迷队伍就可表示为一个具有m个S和n个X的字符串,显然这样的字符串共有
个,其中不满足问题要求的串一定存在一