算法分析大作业.docx
《算法分析大作业.docx》由会员分享,可在线阅读,更多相关《算法分析大作业.docx(13页珍藏版)》请在冰豆网上搜索。
算法分析大作业
算法分析大作业
动态计划方法解
乘法表问题和汽车加油行驶问题
1.动态计划解乘法表问题
1.1问题描述------
1.2算法设计思想------
1.3设计方法------
1.4源代码------
1.5最终止果------
2.动态计划解汽车加油行驶问题
2.1问题描述------
2.2算法设计思想------
2.3设计方法------
2.4源代码------
2.5最终止果------
3.总结
1.动态计划处理乘法表问题
1.1问题描述
定义于字母表∑{a,b,c)上乘法表如表所表示:
依此乘法表,对任一定义于∑上字符串,合适加括号表示式后得到一个表示式。
比如,对于字符串x=bbbba,它一个加括号表示式为(b(bb))(ba)。
依乘法表,该表示式值为a。
试设计一个动态计划算法,对任一定义于∑上字符串x=x1x2…xn,计算有多少种不一样加括号方法,使由x导出加括号表示式值为a。
1.2算法设计思想
设常量a,b,c分别为1,2,3。
n为字符串长度。
设字符串第i到第j位乘积为a加括号法有result[i][j][a]种,
字符串第i到第j位乘积为b加括号法有result[i][j][b]种,
字符串第i到第j位乘积为c加括号法有result[i][j][c]种。
则原问题解是:
result[i][n][a]。
设k为i到j中某一个字符,则对于k从i到j:
result[i][j][a]+=result[i][k][a]*result[k+1][j][c]+result[i][k][b]*result[k+1][j][c]+result[i][k][c]*result[k+1][j][a];
result[i][j][b]+=result[i][k][a]*result[k+1][j][a]+result[i][k][a]*result[k+1][j][b]+result[i][k][b]*result[k+1][j][b];
result[i][j][c]+=result[i][k][b]*result[k+1][j][a]+result[i][k][c]*result[k+1][j][b]+result[i][k][c]*result[k+1][j][c];
输入:
输入一个以a,b,c组成任意一个字符串。
输出:
计算出加括号方法数。
1.3设计方法
乘法表问题直观了解就是经过加括号使得最终运算结果为a,该问题与矩阵连乘问题类似,矩阵连乘是每一次加括号选择运算量最小,写成数学表示式有:
而乘法表问题则是计算全部加括号运算结果为a情况数,并不要求输出加括号方法。
那么能够从乘法最小单元两个符号相乘全部可能开始,接着在两个符号相乘基础上计算三个符号相乘全部可能。
直到计算N长度符号1-N全部可能。
能够定义一个三维数组a[n][n][3],n为输入字符串长度,a[i][j][0]为从字符串中第i个元素到第j个元素子串表示式值为a加括号方法数,a[i][j][1]为从字符串中第i个元素到第j个元素子串表示式值为b加括号方法数,a[i][j][2]为从字符串中第i个元素到第j个元素子串表示式值为c加括号方法数。
由乘法表定义则可知啊a[i][j][0]=(对k求和,k从i到j-1)a[i][k][0]*a[i][k+1][2]+a[i][k][1]*a[i][k+1][2]+a[i][k][2]*a[i][k+1][1];
同理可得到a[i][j][1]和a[i][j][2]。
同时由上面表示式可知,要计算a[i][j][],需先计算a[i][k][]和a[i][k+1][],这里k从i到j-1,故a[i][j][]可采取以下计算次序
1.4源代码
#include"iostream"
#include"algorithm"
#include"fstream"
usingnamespacestd;
/*
f[i][j][0]表示在ch[i]~ch[j]之间以某种方法加括号后,结果为a
f[i][j][1]表示在ch[i]~ch[j]之间以某种方法加括号后,结果为b
f[i][j][2]表示在ch[i]~ch[j]之间以某种方法加括号后,结果为c
a=a*c||b*c||c*a
b=a*a||a*b||b*b
c=b*a||c*b||c*c*/
intf[50][50][3];
charchars[3]={'a','b','c'};
intmul(intn,charch[])
{
for(inti=0;ifor(intk=0;k<3;k++)
f[i][i][k]=(ch[i]==chars[k]?
1:
0);
/*
a=a*c||b*c||c*a
b=a*a||a*b||b*b
c=b*a||c*b||c*c
*/
for(intr=1;rfor(i=0;i{
intj=i+r;//区间右端点
for(intk=i;k{
f[i][j][0]+=f[i][k][0]*f[k+1][j][2]+f[i][k][1]*f[k+1][j][2]+f[i][k][2]*f[k+1][j][0];
f[i][j][1]+=f[i][k][0]*f[k+1][j][0]+f[i][k][0]*f[k+1][j][1]+f[i][k][1]*f[k+1][j][1];
f[i][j][2]+=f[i][k][1]*f[k+1][j][0]+f[i][k][2]*f[k+1][j][1]+f[i][k][2]*f[k+1][j][2];
}
}
returnf[0][n-1][0];
}
intmain()
{
ifstreamfin("mul.txt");
cout<<"输入字符串:
";
charch[100];
fin>>ch;cout<intn=strlen(ch);
cout<<"\n结果为a加括号方法为:
"<fin.close();
return0;
}
1.5最终止果
2.动态计划处理汽车加油行驶问题
2.1问题描述
给定一个N*N方形网络,设其左上角为起点○,坐标为(1,1),X轴向右为正,Y轴向下为正,每个方格边长为1。
一辆汽车从起点○出发驶向右下角终点,其坐标为(M,N)。
在若干网格交叉点处,设置了油库,可供汽车在行驶途中,可供汽车在行驶途中加油。
汽车在行驶过程中应遵守以下规则:
(1)汽车只能沿网格边行驶,装满油后能行驶K条网格边。
出发时汽车已装满油,在起点与终点处不设油库。
(2)当汽车行驶经过一条网格边时,若其X坐标或Y坐标减小,则应付费用B,不然免付费用。
(3)汽车在行驶过程中遇油库则应加满油并付加油费用A。
(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费A)。
(5)
(1)~(4)中各数N,K,A,B,C均为正整数。
2.2算法设计思想
这个题目,应该说是刚开始时候被她给吓到了,只是想着怎样去把全部条件结构起来,然后使用网络流方法来处理问题!
不过,假如真是很冷静下来好好地思索这道题目,就会发觉假如没有那些限制条件,就是一个求解最长路题目,这么就能够直接使用SPFA来处理这个问题!
关键就是在于这个每次最多只能走K个网格边,这么就是限制了活动范围,使得有边无法扩展!
所以能够考虑使用这个分层思想最短路问题!
就是经过将每一个点进行拆分,这么,就是相当于一个分类讨论方法!
而分类讨论了以后,就知道哪些边是能够扩展,哪些边是不能扩展!
关键点就是在于该怎样选择变量来分层,这就是因情况而异了!
像这道题目之中,就是经过油量多少来扩展边!
分层思想,说穿了其实就是相当于这个动态计划之中增加变量方法来确定状态一样,她们实质其实都是一样!
2.3设计方法
采取是动态计划思想来解题,用备忘录方法进行递归,递归式子后面写出.
不能直接以汽车行驶费用为目标来进行动态计划,因为最优子结构性质得不到证实.
所以必需把油量和费用一起考虑,作为动态计划对象,此时就有了最优子结构性质.
最优子结构性质证实
证实:
假设路径M是从起点◎到终点▲一条最小费用路径,P(x,y)是M经过一个点(非加油站),且油量和费用为(g,c),现假设有一条新路径Q从起点◎到点P(x,y),使得其在P(x,y)点油量和费用为(g,c'),其中c'备忘录递归
刚开始时候为每个网格点P(x,y)建立一个统计,初始化时,为该统计存入一个特殊值W,表示汽车未行驶过.那么在汽车行驶过程中,对每个待求汽车最小费用值COST,先查看其对应统计项C,假如存放是初始值W,那么表示这个点P(x,y)是第一次碰到,此时计算出该点最小费用值,并保留在其对应统计项中,以备以后查看.若统计项C中存放不是初始值W,那么表示该问题已经求解过了,其对应统计项中存放就是该点最小费用值COST,此时要取出统计项C值跟最新计算出COST进行比较,取其最小那个数存入到C中.依此建立统计项C值,当程序递归完成时,我们也得到了汽车行驶到(n,n)最小费用值COST.
2.4源代码
#include"iostream"
#include"algorithm"
#include"fstream"
usingnamespacestd;
#defineINF10000
/*
f[i][j][0]表示汽车从网格点(1,1)行驶至网格点(i,j)所需最少费用
f[i][j][1]表示汽车行驶至网格点(i,j)还能行驶网格边数
s[i][0]表示x轴方向
s[i][1]表示y轴方向
s[i][2]表示行驶费用
f[i][j][0]=min{f[i+s[k][0]][[j+s[k][1]][0]+s[k][2]}
f[i][j][1]=f[i+s[k][0]][[j+s[k][1]][1]-1;
f[1][1][0]=0
f[1][1][1]=K
f[i][j][0]=f[i][j][0]+A,f[i][j][1]=K假如(i,j)是油库
f[i][j][0]=f[i][j][0]+C+A,f[i][j][1]=K(i,j)不是油库,且f[i][j][1]=0
*/
intN;//方形网络规模
intA;//汽车在行驶过程中碰到油库应加满油并付加油费A
intC;//在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费A)
intB;//当汽车行驶经过一条网格边时,假如其x坐标或y坐标降低,应付费用B
intK;//装满油后,还能行驶K条边
intf[50][50][2];
ints[4][3]={{-1,0,0},{0,-1,0},{1,0,B},{0,1,B}};
inta[50][50];//方形网络
intdyna()
{
inti,j,k;
for(i=1;i<=N;i++)
{
for(j=1;j<=N;j++)
{
f[i][j][0]=INF;
f[i][j][1]=K;
}
}
f[1][1][0]=0;
f[1][1][1]=K;
intcount=1;
inttx,ty;
while(count)
{
count=0;
for(i=1;i<=N;i++)
{
for(intj=1;j<=N;j++)
{
if(i==1&&j==1)
continue;
intminStep=INF;
intminDstep;
intstep,dstep;
for(k=0;k<4;k++)//可走四个方向
{
tx=i+s[k][0];
ty=j+s[k][1];
if(tx<1||ty<1||tx>N||ty>N)//假如出界
continue;
step=f[tx][ty][0]+s[k][2];
dstep=f[tx][ty][1]-1;
if(a[i][j]==1)//假如是油库
{
step+=A;
dstep=K;
}
if(a[i][j]==0&&dstep==0&&(i!
=N||j!
=N))//假如不是油库,且油已经用完
{
step+=A+C;
dstep=K;
}
if(step{
minStep=step;
minDstep=dstep;
}
}
if(f[i][j][0]>minStep)//假如有更优解
{
count++;
f[i][j][0]=minStep;
f[i][j][1]=minDstep;
}
}
}
}
returnf[N][N][0];
}
intmain()
{
ifstreamfin("car.txt");
cout<<"输入方格规模:
";
fin>>N;cout<cout<<"\n输入装满油后能行驶网格边数:
";
fin>>K;cout<cout<<"\n输入加油费:
";
fin>>A;cout<cout<<"\n输入坐标降低时应付费用:
";
fin>>B;cout<
cout<<"\n输入增设油库费用:
";
fin>>C;cout<cout<<"\n输入方形网络:
\n";
for(inti=1;i<=N;i++)
{
for(intj=1;j<=N;j++)
{
fin>>a[i][j];
cout<}
cout<}
cout<<"最优行驶路线所需费用为:
"<fin.close();
return0;
}
2.5最终止果
3.总结
动态计划(DynamicProgramming,DP)思想启发于分治算法思想,也是将复杂问题化解若干子问题,先求解小问题,再依据小问题解得到原问题解。
不过DP与分治算法不一样是,DP分解若干子问题,往往是相互不独立,这时假如用分治算法求解,那么会对重合子问题反复进行求解,从而使得浪费大量时间。
那么假如我们保留已经计算过子问题解,这么当再次计算该子问题时,能够直接使用,这么能够节省大量时间。
设计动态计划四个步骤:
1、刻画一个最优解结构特征。
2、递归地定义最优解值。
3、计算最优解值,通常采取自底向上方法。
4、利用计算出信息结构一个最优解。