算法分析与设计课程设计报告Word文件下载.docx
- 文档编号:16973740
- 上传时间:2022-11-27
- 格式:DOCX
- 页数:23
- 大小:317.48KB
算法分析与设计课程设计报告Word文件下载.docx
《算法分析与设计课程设计报告Word文件下载.docx》由会员分享,可在线阅读,更多相关《算法分析与设计课程设计报告Word文件下载.docx(23页珍藏版)》请在冰豆网上搜索。
根据计算最优值时得到的信息,构造最优解。
给定金额n以及1,2,5分硬币,求找n的最少硬币数。
对于大于1分的找零理所当然可以找零1分,大于2分和5分的找零与此类似,那么找零时就有三种选择,即找零后剩余金额为n-1,n-2,n-5,这三种选择总的找零数最少的方案即为所求解的方案。
显然,最终的找零方案与执行当前选择后剩余的金额的找零相关,即金额n的最优找零方案包含了金额n-1,n-2,n-5的最优找零方案,于是可得如下状态转移方程:
具体的求解过程:
初始化F
(1)=1,F
(2),F(5)=1
F
(1)
1
F
(2)
F(3)
min{F
(2)+1,F
(1)+1}=2
F(4)
min{F
(2)+1,F(3)+1}=2
F(5)
F(6)
min{F
(1)+1,F(4)+1,F
(1)+1}=2
…
F(n)
min{F(n-1)+1,F(n-2)+1,F(n-5)+1}
算法设计思路及求解过程
思路:
题目所描述的整个过程是“并行的”,而且所有人到达各自楼层的用时只与最晚到达的人有关。
由于去各个楼层的具体数目对结果没有影响,所以可以将“电梯还剩i个人”表述成“电梯里面的乘客还要去i个楼层”。
电梯从第1层开始向上运行,任意时刻的状态都可以由“电梯当前所处楼层”和“电梯里面都有哪些乘客确定”,初始状态为“电梯在1楼”和“所有乘客都在电梯上”。
在电梯运行的每一个阶段都需要作出相应的决策,哪些乘客乘坐电梯到目的层,哪些乘客选择爬楼梯到达目的地。
决策后,电梯里面的乘客被分成两部分:
乘客留在电梯里面继续上升;
乘客离开电梯走楼梯到达。
求当前状态下电梯里面的乘客所有人到达目的所需要的最短时间,只需要找到一个最优决策,使得下电梯的乘客和留在电梯中的乘客最晚到达时间越短越好,这个最短时间就是当前状态下的最优解。
如果假设决策后留在电梯里面的乘客到达各自楼层所需要的时间为T1,离开电梯的各自到达目的地所需时间为T2,则min{max(T1,T2)}就是当前状态的最优解,其中T1可以由“当前层数+1”和“决策后剩下的人”确定的状态得到;
T2则为下电梯走楼梯到达目的走楼梯最多的那一位乘客所花时间。
如果去第k层的乘客选择在当前楼层下电梯,那么第1,2…k-1层的乘客也应该选择在此时下电梯(如图1所示),这样就可以得到当前决策下的一个最优解。
为了进一步处理停靠请求,对楼层按从高到低进行排序,并以此进行编号,如此可以避免在求解过层中处理不连续的请求,如:
图1解题结论
4,5,10。
使用i,j两个参数表示电梯当前的状态,即电梯在第i层,电梯中有j位乘客。
综上所述,可得如下状态转移方程:
f(i,j)表示电梯在第i层楼时,电梯中j个人都到达目的地所需要的最短时间。
具体求解过程:
第一步:
计算初始状态f(topFloor,1),f(topFloor,2),…,f(topFloor,n);
第二步:
根据状态转移方程计算f(i,j);
第三步:
根据计算最优值时记录的信息求解最优解。
1.3算法程序流程
图2Input函数流程图
图3solve函数流程图
图4main函数流程图
图5calculate函数流程图
图6tStay函数流程图
图7tLeave函数流程图
1.4算法的程序实现代码
#include<
iostream>
cstring>
algorithm>
#include<
cmath>
limits>
vector>
usingnamespacestd;
constintmaxN=30,maxF=31;
//电梯上一层楼所需时间,电梯每次停靠时长,人走一层楼所需时间
constintve=4,st=10,vw=20;
intn,f[maxN+1];
//数据读取
boolinput(){
cin>
>
n;
if(n==0)returnfalse;
//注意:
f[1...n]中楼层数从高到低排列
for(inti=n;
i>
=1;
i--)
f[i];
returntrue;
}
intdp[maxF+1][maxN+1],nextJ[maxF+1][maxN+1];
//目前电梯在第currF层,第L层到第R层乘客离开电梯
//函数返回这些离开电梯的乘客中最晚到达目的层所需时间
inttLeave(intcurrF,intl,intr){
if(l>
r)return0;
//仅需考虑两端,无论此刻电梯在何处,第l-r层花时间最多的
//一定是离电梯当前所在楼层最远的乘客
returnmax(abs(currF-f[l]),abs(currF-f[r]))*vw;
//现在电梯在第i层,电梯里面本来有j位乘客,离开电梯的乘客剩下jj位
inttStay(inti,intj,intjj){
//没有乘客离开,电梯不停
if(j==jj||i==1)
returndp[i+1][jj]+ve;
//所有人都离开电梯
elseif(jj==0)
return0;
//一般情况,电梯在第i层停靠
else
returndp[i+1][jj]+ve+st;
//
voidcalculate(){
//边界:
电梯在顶楼时所有人都必须下电梯
inttopFloor=f[1];
for(intj=1;
j<
=n;
j++){
//1,j表示停靠请求的编号,编号越小表示编号停靠楼层越高
dp[topFloor][j]=tLeave(topFloor,1,j);
}
for(inti=topFloor-1;
i--){
//i表示电梯此刻所在位置
dp[i][j]=numeric_limits<
int>
:
max();
for(intjj=0;
jj<
=j;
jj++){
//计算离开电梯的人和留在电梯里面的人中到达目的地最晚的
inttmp=max(tStay(i,j,jj),tLeave(i,jj+1,j));
//在此求解花费时间最短的乘客
if(dp[i][j]>
tmp){
dp[i][j]=tmp;
//jj以前的乘客均离开电梯
nextJ[i][j]=jj;
cout<
<
dp[1][n]<
endl;
//重构最优解
voidrebuildSolution(){
vector<
stops;
intj=nextJ[1][n],topFloor=f[1];
for(inti=2;
i<
=topFloor;
i++){
if(nextJ[i][j]!
=j){
stops.push_back(i);
j=nextJ[i][j];
if(j==0)break;
stops.size();
for(inti=0;
"
"
stops[i];
voidsolve(){
memset(dp,0,sizeof(dp));
memset(nextJ,0,sizeof(nextJ));
calculate();
rebuildSolution();
题目2切割木材
2.1题目描述
一个木匠从木材公司买了一批木材,每块木材的长度均相同,但由于制作家具时所需的木块长度各不相同,因此需要把这些木材切割成长度不同的木块。
同时每次切割时由于锯子本身有一定的宽度,因此每切割一次就会浪费掉一些木料。
请设计一个程序使木匠能够用最少的木材切割出所需的木块。
输入描述:
输入有若干个测试样例,每个测试样例占一行。
每行由若干个整数构成,第一个整数为所购买的木块的长度L(0<
L<
=30000),第二个整数为锯子的宽度W(0<
W<
=1000),其后的若干个整数分别表示制作家具时需要的木块的长度。
输出描述:
每个测试样例输出一行,为一个整数N,表示制作家具时需要购买的木块的数量。
样例输入:
10001002502505006501000
100050200250250500650970
样例输出:
3
4
2.2算法文字描述
此题目是装载问题的一个变种,与装载问题不同的是此问题没有给出“船”数量,但是给出了船的载重量,因此仍旧可以借鉴解装载问题的思路,即让每一根原材料可以切出更多符合要求的木料,类似于装载问题中“将第一艘轮船尽可能地装满”,即保证切割以后剩余的原材料是最少的。
算法具体描述如下:
Step1:
声明求解结果变量res=0,剩余未切割木料数量count=n,当前已切割木料长度和cw=0,目前最大切割长度bestW=0,求解标记数组visited[n],当前最优求解数组nVisited[n],问题求解状态记录数组res_arr[n],锯口宽度sw;
Step2:
当剩余未切割木料数量count大于0时,利用回溯法进行最大子集和求解。
当i<
n-1时,搜索左子树的条件:
当前节点未被访问且cw+data[i]<
=w+sw,访问左子树时第i层相应节点时将相应访问标记visited[i]置为true,递归搜索该节点的左子树;
递归搜索右子树时,将当前节点访问标记visited[i]置为false;
Step3:
当i>
n-1时,获得一种切割方案,若此次求解结果优于已得求解结果,即bestW<
cw,使用nVisited数组记录当前求解状态,同时更新bestW的值;
Step4:
利用回溯法完成1次木料切割后,更当前问题求解状态res_arr数组,根据最新的求解状态更新未切割木料数量count,同时res++,若count=0则求解结束,否则重复2,3,4直至count=0。
2.3算法程序流程
图8main函数流程图
图9input函数流程图
图10solve函数流程图
图11backtrack函数流程图
2.4算法的程序实现代码
stdio.h>
malloc.h>
#defineMAX_SAMPLE_LENGTH50
/*回溯法求解*/
int*in=(int*)malloc(MAX_SAMPLE_LENGTH*sizeof(int));
int*data=(int*)malloc(MAX_SAMPLE_LENGTH*sizeof(int));
bool*visited=(bool*)malloc(MAX_SAMPLE_LENGTH*sizeof(bool));
bool*nVisited=(bool*)malloc(MAX_SAMPLE_LENGTH*sizeof(bool));
bool*res_arr=(bool*)malloc(MAX_SAMPLE_LENGTH*sizeof(bool));
intw;
//原材料长度
intn;
//数据元素个数
intsw;
//锯口宽度
intcw;
//当前已锯木头长度和
intres;
//求解结果
intbestW;
//当前求解最大值
boolflag=true;
//初始化数据保存数组
memset(in,0,MAX_SAMPLE_LENGTH*sizeof(int));
memset(visited,false,MAX_SAMPLE_LENGTH*sizeof(bool));
memset(res_arr,false,MAX_SAMPLE_LENGTH*sizeof(bool));
memset(nVisited,false,MAX_SAMPLE_LENGTH*sizeof(bool));
//记录输入数据个数
n=0;
//读取数据-原材料(木头)长度
scanf("
%d"
&
w);
if(0==w)flag=false;
sw);
while(flag){
data+n);
n++;
charch=getchar();
if(ch=='
\n'
)break;
returnflag;
voidbacktrack(inti,intk){
if(i>
n-1){
if(bestW<
cw){
//记录最优值
bestW=cw;
//记录当前最优解
nVisited[i]=visited[i];
return;
//进入右子树条件
if(!
res_arr[i]&
&
cw+data[i]+k*sw<
=w){
//记录当前已锯木头数量
k++;
//进入右子树实际操作
cw+=data[i];
//访问标记
visited[i]=true;
backtrack(i+1,k);
cw-=data[i];
k--;
visited[i]=false;
intsolve(int*data,intn){
res=0;
//求解结果初始化
intcount=n;
while(count>
0){
//初始化,cw当前已锯木头长度和,
//count剩余未锯木头数量,bestW本次求解最大长度和
cw=0,count=0,bestW=0;
backtrack(0,0);
//更新待解决问题状态
res_arr[i])
res_arr[i]=nVisited[i];
//剩余未求解元素个数
res_arr[i])count++;
//记录求解结果
res++;
returnres;
intmain(){
while(input()){
solve(data,n);
printf("
\n%d\n"
res);
题目3设计题
3.1题目描述
给定一个数塔,如图12所示。
在此数塔中,从顶部出发,在每一节点可以选择向左走还是向右走,一直走到底层。
请找出一条路径,使路径上的数值和最大。
图12数塔
3.2输入要求
第一行输入一个整数n,n表示数塔的层数,第2,…,n+1行为数塔对应节点的数值。
3.3输出要求
第一行输出路径数值和,第二行输出具体路径。
3.4样例输入
5
13
1112
40716
614158
127132411
3.5样例输出
91
(1,1)->
(2,1)->
(3,1)->
(4,2)->
(5,3)
3.6测试样例输入
7
11
98
12107
4152319
178211216
322524283127
85910111315
3.7测试样例输出
113
(3,2)->
(4,3)->
(5,3)->
(6,4)->
(7,5)
3.8算法实现的文字描述
动态规划采用自底向上逐层分阶段决策
1)第1次决策,针对第4层
如果最优路径经过6,则从第4层到第5层应该经过12,则第4+第5层的最大路径为6+12=18
如果最优路径经过14,则从第4层到第5层应该经过13,则第4+第5层的最大路径为13+14=27
……
这样实际上将5阶数塔变为4阶数塔问题了。
2)逐层向上递推,最后得到问题的最优解
根据题意,待处理的数据规模同数塔的层数有关,同时数据节点的个数与层数相同,所以可以使用二维数组data存储待处理节点数值,只有一半的节点有数值,实际上是一个下三角矩阵。
可以较为容易地得到问题状态转移方程:
声明变量数塔层数n,待处理数据节点数值数组data[n][n],结果(状态)数组d[n][n];
输入数塔层数n,维数组data和d分配存空间;
初始化第n层结果(状态)数组值;
根据状态转移方程求解d(i,j),其中i从n-1到1,j从1到i;
Step5:
输出d(1,1);
Step6:
根据数组d求解具体路径并输出。
3.9算法程序流程
图13main函数流程图
图14tower_walk函数流程图
3.10算法的程序实现代码
math.h>
int*data=NULL;
//存储数塔原始数据
int*dp=NULL;
//状态值记录
//塔的层数
/*动态规划实现数塔求解*/
voidtower_walk()
{
intindex1=0,index2=0;
//dp初始化
for(inti=0;
i<
n;
++i)
{
index1=(n-1)*n+i;
dp[index1]=data[index1];
inttemp_max;
for(inti=n-2;
i>
=0;
--i)
for(intj=0;
j<
=i;
++j)
//使用递推公式计算dp的值
index1=(i+1)*n+j;
index2=i*n+j;
temp_max=(dp[index1]>
dp[index1+1])?
dp[index1]:
dp[index1+1];
dp[index2]=temp_max+data[index2];
/*打印最终结果*/
voidprint_result()
intindex=0;
%d\n"
dp[index]);
intnode_value;
//首先输出塔顶元素
intj=0,i=0;
(%d,%d)"
i+1,j+1);
for(i=1;
index=(i-1)*n+j;
node_value=dp[index]-data[index];
//如果node_value==dp[i][j]则说明下一步应该是data[i][j];
//如果node_value==dp[i][j+1]则说明下一步应该是data[i][j+1].
index=i*n+j+1;
if(node_value==dp[index])++j;
->
\n"
);
intmain()
n);
data=(int*)malloc(n*n*sizeof(int));
memset(data,0,n*n*sizeof(int));
dp=(int*)malloc(n*n*sizeof(int));
memset(dp,0,n*n*sizeof(int));
index=i*n+j;
data+index);
tower_walk();
print_result();
算法分析与设计课程总结
通过本课程各个专题模块的学习,回顾和巩固了曾经学习过的知识,并且有了新的感受和收获。
老师的教学具有很强的启发性,在整个学习过程中,通过具体的示例详细地讲解了穷举法、动态规划、贪心算法、回溯法和分支限界法等
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 分析 设计 课程设计 报告