分酒问题程序报告.docx
《分酒问题程序报告.docx》由会员分享,可在线阅读,更多相关《分酒问题程序报告.docx(19页珍藏版)》请在冰豆网上搜索。
分酒问题程序报告
合肥学院
计算机科学与技术系
1、问题分析和任务定义
题目:
已知有3个容量分别为3kg,5kg和8kg且没有刻度的酒瓶3kg和5kg的瓶子均装满了酒。
而8kg的瓶子为空。
现要求仅用这3个酒瓶将这些酒均分为两个4kg,并分别装入5kg和8kg的瓶子中。
若要完成题目的要求,首先需要选择一个数据结构表示酒杯数,有3个酒杯并且通过子函数可以输入容器的容量,由题意可知输入的酒杯的容量分别为3、5、8,起始状态下酒杯的装酒数为3、5、0,最终经过转换变为0、4、4。
其中每个酒杯的装酒数不得超过容器的容量,可以采取回溯递归的思路。
每经过一次转换就覆盖前面的状态,直至最终变为需要的状态为止。
2、数据结构的选择和概要设计
1.数据结构的选择
模型描述:
由于涉及状态之间的转化,因此可以用图模型进行求解。
把每次可分的状态抽象为一个图结点,按照图的有关知识去求解。
本题采用邻接表法存储图的各个顶点信息,然后用深度优先搜索遍历的方法,得到所有的解。
整个程序的实现较简单,属于图应用的范畴。
因此在遇到求解搜索路径的问题时,我们不妨从图模型的角度去考虑问题,把有关问题抽象为图模型。
通过图模型去思考该类问题地解法可能会更简单。
因此要深刻理解图方面的知识,尤其是图的深度优先搜索和图的广度优先搜索的应用。
提供的思路为回溯算法的设计方法:
回溯是一种系统的搜索问题解答的方法(常见的是求解迷宫老鼠的问题);其过程主要有3个步骤:
(1)首先要为问题定义一个解空间,这个解空间包含问题的解(可能是最优解)。
(2)组织解空间以便能被容易的搜索,同时所搜方法要能够避免移动到不可能产生解得子空间。
(3)定义了解空间的组织方法,这个空间即可按深度优先的方法从开始结点。
从扩展结点可移动到一个新结点,如果能从当前的当前的结点移动到一个新结点,那么这个新结点将变成一个活结点和新的扩展结点,原扩展结点就可以压入堆栈中,但仍是一个活结点。
如果不能移动到一个新结点,当前的扩展结点就是一个死结点,那么就只能返回到最近被考察的活结点(回溯,依据堆栈),这个活结点就变为新的扩展结点。
按照这种方法进行搜索,直到找到出口为止,或者是栈空。
由于堆栈中始终包含从入口到当前位置的路径,如果最终找到了出口,那么堆栈中保留的位置值所示的路径就是所要求的问题解之一;如果最终栈空,则表示不存在求解的路径。
也就是当我们找到所有的答案或者回溯了所有的活结点时,搜索过程结束。
回溯算法的解空间可以是图、树、矩阵等数据结构。
2.概要设计
设计本题算法的构思如下:
(1)为搜索除符合条件的简单路径,需要按深度优先搜索方式进行遍历。
因此,求解算法应是深度遍历算法的变形形式,因而也是递归是形式的算法。
(2)由于要求遍历序列中的各结点按次序构成一条简单路径,因此,本算法与深度遍历算法有明显的不同:
并非任意选择的起点和访问次序都能得到解。
而本题的起点和终点是确定的。
(3)既然要在求解过程中进行试探,则需要记录试探的中间状态;某顶点是否在当前试探路径中,已经试探的各顶点和当前试探顶点的信息。
将所用的变量及有关参数设置如下:
用邻接链表存储所得图的结构,链表用两个结构体表示,(structarc_node,structvex_node),其中每个链表的头保存在一个数组中。
用布尔数组visited[MAX_status]表示各状态顶点是否在当前路径中(初始状态全为false).
用栈stack存储当前路径中的各顶点。
(4)既然是试探型求解,则需要对当前顶点v0的每个邻接点进行试探(程序中用s[v0].next表示),试探由v0经s[v0].next往下是否可以得到解,每个s[v0].next都有可能成功(指现在可以放在路径上,这包括暂时的和最终的)与失败的(指状态转化不能完成),,对此应分别做不同的处理:
若试探成功,则应将s[v0].next放在路径中,。
然后再由其往下求解。
若不成功,则应恢复s[v0].next的用关信息,以使s[v0].next在试探其他路径中成为可选的试探点。
(5)为了能求出解以及所有可能的解,需要做如下的工作:
选择路径:
本题中的起点已经限定,即start_vex={3,5,8}
搜索路径:
从v往下搜索时,应依次选择v的所有不在当前路径中的邻接点往下搜索。
为此,需要有这方面的保证:
应在试探某顶点之后并在换下一个试探顶点的前恢复该顶点的有关状态,以使其重新成为可选择的顶点。
(6)由以上讨论得本算法的基本思想:
当访问的到顶点状态v==end_vex(最终状态)时,则说明已经求得一解,因此可输出结果,并结束本次算法,继续求解其他可能的解的情况。
若v!
=end_vex,则依次选择v的所有不在当前试探路径中邻接点s[v0].next往下搜索,这包括以下的操作:
试探:
将s[v0].next放在栈stack中通过push()函数完成,并置visited[s[v0].next]为true,然后以s[v0].nex为起点往下搜索。
恢复:
将s[v0].next恢复为不在当前的路径中,以使在试探其他路径时可用。
(7)有关算法中的变量设置:
intcap[N]表示各个酒杯的最大容量;
intrest[N]表示各个酒杯当前的容量;(全局变量)
intend[N]表示各个酒杯最后的容量状态;(全局变量)
inttemp[N]表示辅助数组,(全局变量)
boolvisited[MAX_STATUS]进行深度优先搜索遍历的标志数组;(全局变量)
intstates表示所有的状态个数;(全局变量)
sum表示所有解的个数(全局变量)
start_vex表示开始的状态点(全局变量)
end_vex表示结束的状态点(全局变量)
vex_nodes[MAX_STATUS]存储图节点的邻接链表
3、详细设计和编码
由题意可知实验的酒杯的容积分别为3、5、8,起始的状态为3、5、0,最终的状态为0、4、4。
从起始状态每经过一次转换就会产生新的状态(如3、5、0→3、0、5)。
较各种可能存在的状态,并将新的状态入栈,再转换新的状态,直到可以转换到最终状态,则输出该路径;如果没有新的状态则将该状态出栈,返回上一状态并寻找新的状态结点,按此过程依次遍历,直到找出所有路径为止。
用栈stack存储当前路径中的各顶点。
出栈、入栈、置空栈
用堆栈来存储路径的过程,将各种状态一一入栈,如过某种状态没有新的转换,将其出栈并返回前一节点。
以下为栈的出栈、入栈、置空栈:
作用在于在后面得模块中调用该子模块,可以实现不同状态得入栈和出栈,最终保存整个可能实现得路径过程。
voidpush(intn)
{stack[top++]=n;}
intpop()
{returnstack[--top];}
boolisempty()
{returntop==0;}
用产生得新状态节点与原有得比较(通过比较函数comp()实现),在比较各种可能存在的状态,如果状态存在则仅仅加入邻接表,如果不存在则加入状态表,并加入邻接表。
最终可以实现出所有得可能存在得状态节点;
其过程如下:
boolcomp(inta[],intb[])//比较状态
{
inti;
for(i=0;i{if(a[i]!
=b[i])returntrue;}
returnfalse;
}
voidstore_state(intnumber)
{//保存状态,如果状态存在则仅仅加入邻接表,如果不存在则加入状态表,并加入邻接表
arc_node*temp;
inti;
for(i=0;i<=states;i++)
{
if(!
comp(rest,s[i].status))
break;
}
if(i>states)
{
states++;
memcpy(s[states].status,rest,sizeof(int)*N);
s[states].next=NULL;
if(!
comp(rest,end))
end_vex=i;
i=states;
}
temp=newarc_node;//建邻接链表存储所得图的结构
temp->number=i;
temp->next=s[number].next;
s[number].next=temp;
}
创建初始状态,设定程序的条件:
这一部分得作用在于通过自己输入容器得容积,并设立好容器中得酒数得初始状态和结束状态。
通过转换实现最终由初始状态到最终状态得所有可能途径。
voidcreate_status()//建立状态的函数
{
intnumber,i;
cout<<"输入各个容器的容积:
";
for(i=0;icin>>cap[i];
cout<<"输入对应容器的起始状态:
";
for(i=0;icin>>s[0].status[i];
cout<<"输入对应容积的最终状态:
";
for(i=0;icin>>end[i];
number=0;states=0;start_vex=0;sum=0;
s[0].next=NULL;
while(number<=states)
{
memcpy(rest,s[number].status,sizeof(int)*N);
add_state(number);
number++;
}
for(number=0;number<=states;number++)
visited[number]=false;
}
输出函数,用来输出函数的结果,显示主要路径
此部分作为输出函数得设立,没有具体得要求,尽量利用空格,文字说明使得程序在输出结果时显得简洁明了。
便于观察试验结果
voiddisplay_path()//在dfs中输出路径
{
inti,j,k;
cout<<"得到的方案"<";
for(i=0;i{
j=stack[i];
for(k=0;kcout<
cout<<"";
}
cout<}
最重要的为遍历算法的过程:
这是程序得核心内容,此部分利用深度优先遍历,搜索出由初始状态到最终状态得所有可行路径。
具体实现过程是:
将初始状态入栈,遍历搜索,寻找新的状态节点,并将其入栈,有2种结果:
若最终实现最终状态,则该路径正确,为可行路径,在结果中输出该路径。
则为找寻成功;若程序找寻不成功(即找不到新得节点),返回上一节点,找寻新得路径,直至实现所有可能存在得路径为止。
其算法实现如下:
voiddfs(intv0)//深度优先搜索遍历,得到符合条件的路径
{
if(!
visited[v0])
{
if(v0==end_vex)
{
sum++;
display_path();
}
else
{
arc_node*temp;
temp=s[v0].next;
visited[v0]=true;
while(temp)
{
push(temp->number);//加到栈中,以得到所有的解
dfs(temp->number);
pop();
temp=temp->next;
}
visited[v0]=false;
}
}
}
主函数main()调用上述的各子模块实现程序。
4、上机调试
1.语法错误及修改:
本算法使用回溯、递归的思想进行遍历,相对得到一些简化。
但本人能力有限可能仍不是最佳的性能,可能其中存在一些复杂的地方,但经过调试能成功的运行,不存在语法上的错误。
2.在试验中关于头文件不是很理解,经过上网搜索,对的理解如下:
String类是不可变(final)的,对String类的任何改变,都是返回一个新的String类对象.这样的话把String类的引用传递给一个方法,改方法对String的任何改变,对原引用指向的对象没有任何影响,这一点和基本数据类型相似.
字符串数据类型,可包含单一字元或字符串的变数型态。
需要注意的是在NoahWeb中要指定字符串给字符串变量,要在头尾加上单引号
通过上网搜索解决了这个问题
5、测试结果及其分析
1.依据题意输入相应的信息,程序结果显示如下:
图1程序运行状态初始图
2.输入信息后,按任意键查看结果
图2程序运行结果显示图
3.正确的输出结果,按任意键即可退出该程序
6、用户使用说明
1.根据显示的信息进行相应的操作
2.先输入3个酒瓶的容积(依题意可知为:
3、5、8)
3.输入酒瓶中的酒的初始状态,且酒的数量小于步骤2中输入的酒瓶的容积(依题意可知为:
3、5、0)
4.输入酒瓶中的酒的最终状态,且酒的数量小于步骤2中输入的酒瓶的容积(依题意可知为:
0、4、4)
5.输入完成后按任意键即可查看所有解法的结果
6.成功后可以按任意键退出该程序
7、参考文献
[1]王昆仑,李红等编著.数据结构与算法.北京:
中国铁道出版社,2007.
[2]李春葆,章启俊等编著.C++程序设计学习与上机实验知道.北京:
清华大学出版社
[3]苏仕华编著.数据结构与算法解析.合肥:
中国科学技术大学出版社,2004.
[4]郑莉等著.C++语言程序设计(第三版).北京:
清华大学出版社,2003.
[5]谭浩强等编著.C程序设计(第三版).北京:
清华大学出版社,2005.
[6]徐孝凯编著.数据结构实用教程.北京:
清华大学出版社,1999.
[7]严蔚敏,陈文博编著.数据结构及算法教程.北京:
清华大学出版社,2001.
[8]胡学钢.数据结构与算法设计指导.北京:
清华大学出版社,1999.
8、附录
源代码:
#include
#include
usingnamespacestd;
#defineN3//酒杯数
#defineMAX_STATES100//允许存储的最大状态数
typedefstructarc_node//边节点
{
intnumber;//结点号
structarc_node*next;
}arc_node;
typedefstructvex_node//顶点节点
{
intstatus[N];//存储状态
structarc_node*next;
}vex_node;
intstack[MAX_STATES];
inttop=0;
voidpush(intn)
{stack[top++]=n;}
intpop()
{returnstack[--top];}
boolisempty()
{returntop==0;}
//定义有关变量
intcap[N],rest[N],end[N],temp[N];
vex_nodes[MAX_STATES];//状态表
boolvisited[MAX_STATES];
intstates,sum,start_vex,end_vex;
boolcomp(inta[],intb[])//比较状态
{
inti;
for(i=0;i{if(a[i]!
=b[i])returntrue;}
returnfalse;
}
voidstore_state(intnumber)
{//保存状态,如果状态存在则仅仅加入邻接表,如果不存在则加入状态表,并加入邻接表
arc_node*temp;
inti;
for(i=0;i<=states;i++)
{
if(!
comp(rest,s[i].status))
break;
}
if(i>states)
{
states++;
memcpy(s[states].status,rest,sizeof(int)*N);
s[states].next=NULL;
if(!
comp(rest,end))
end_vex=i;
i=states;
}
temp=newarc_node;
temp->number=i;
temp->next=s[number].next;
s[number].next=temp;
}
voidadd_state(intnumber)//检测出所有可能的变化状态
{
inti,j,rest_i,rest_j;
for(i=0;i{
if(rest[i]==0)continue;
for(j=0;j{
if(j==i)continue;
rest_i=rest[i];
rest_j=cap[j]-rest[j];
if(rest_i<=rest_j)
{
rest[i]=0;
rest[j]+=rest_i;
store_state(number);
rest[i]=rest_i;
rest[j]-=rest_i;
}
else
{
rest[i]-=rest_j;
rest[j]=cap[j];
store_state(number);
rest[i]+=rest_j;
rest[j]-=rest_j;
}
}
}
}
voidcreate_status()//建立状态的函数
{
intnumber,i;
cout<<"输入各个容器的容积:
";
for(i=0;icin>>cap[i];
cout<<"输入对应容器的起始状态:
";
for(i=0;icin>>s[0].status[i];
cout<<"输入对应容积的最终状态:
";
for(i=0;icin>>end[i];
number=0;states=0;start_vex=0;sum=0;
s[0].next=NULL;
while(number<=states)
{
memcpy(rest,s[number].status,sizeof(int)*N);
add_state(number);
number++;
}
for(number=0;number<=states;number++)
visited[number]=false;
}
voiddisplay_path()//在dfs中输出路径
{
inti,j,k;
cout<<"得到的方案"<";
for(i=0;i{
j=stack[i];
for(k=0;kcout<
cout<<"";
}
cout<}
voiddfs(intv0)//深度优先搜索遍历,得到符合条件的路径
{
if(!
visited[v0])
{
if(v0==end_vex)
{
sum++;
display_path();
}
else
{
arc_node*temp;
temp=s[v0].next;
visited[v0]=true;
while(temp)
{
push(temp->number);//加到栈中,以得到所有的解
dfs(temp->number);
pop();
temp=temp->next;
}
visited[v0]=false;
}
}
}
voidfindpath()
{
push(start_vex);//选择深度所搜遍历的起点,已给定
dfs(start_vex);//进行深度所搜遍历
}
voidmain()
{
create_status();//建立模型的状态
cout<<"得到的解法如下:
"<findpath();//输出得到的解
}
课程设计感言
课程设计是对我们一个学期对学习数据结构与算法的总结,是检验我们这个学期的学习的成果,有助于培养我们对学习的知识加以应用。
课程设计是培养学生综合运用所学知识,发现,提出,分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过程
通过2个星期的学习和实验课上的对课程设计的程序的算法的理解,在这个过程中出现了好多的错误和不解,有时甚至不想做下去了。
经过不断的求助以及自己上网搜寻,最终解决了种种地问题,问题解决了但并不能代表自己就弄懂了、理解了。
我们求学应抱着严谨的学习态度,通过向同学求教,查资料最终队程序有了整体的了解和认识。
这次课程设计历时二个星期多左右,通过这两个星期的学习,发现了自己的很多不足,自己知识的很多漏洞,看到了自己的实践经验还是比较缺乏,理论联系实际的能力还急需提高。
在整整两星期的日子里,可以说得是苦多于甜,但是可以学到很多很多的的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。
最终得出了正确的程序时那种涌上心头的喜悦要远比先前的痛苦要强烈的多,看到自己的付出有了收获时的喜悦当然是不言而喻的。
喜欢这种苦尽甘来的感觉了。
还要感谢这些在我的课程设计中给我帮助的指导老师和同学。