TSP问题分析动态规划分支界限法蛮力法教学文案Word文件下载.docx
《TSP问题分析动态规划分支界限法蛮力法教学文案Word文件下载.docx》由会员分享,可在线阅读,更多相关《TSP问题分析动态规划分支界限法蛮力法教学文案Word文件下载.docx(21页珍藏版)》请在冰豆网上搜索。
某一个点i不经过任意点回到起始点的最短路径和为mp[i][0](默认初始点为0)
dp[i][0]=mp[i][0];
(1<
=i<
n)
点i经过集合S(二进制表示的数为j)的最短路径和为从点i经过集合S中的某一点k后再从该点出发,经过集合S-{k}的最小值。
dp[i][j]=min{mp[i][k]+dp[k][j-(1<
<
(k-1))};
2、贪心法
(1)数据结构
利用贪心法每次取最近距离的函数dfs(u,k,l),u表示当前在顶点u,k表示已经走过了k个点,l表示所经过的路径和。
inq[i]表示顶点i已经走过了。
如果当前在顶点u,则取与顶点u距离最近的点p。
dfs(u,k,l)=dfs(p,k+1,l+mp[u][p])
3、分支限界法
结构体
structnode
{
intvisp[22];
//标记哪些点走了
intst;
//起点
inted;
//终点
intk;
//走过的点数
intsumv;
//经过路径的距离
intlb;
//目标函数的值
booloperator<
(constnode&
p)const
{
returnlb>
p.lb;
}
};
优先级队列
priority_queue<
node>
q;
in()
输入函数。
dfs(intu,intk,intl)
利用贪心法每次取最近距离的函数,u表示当前在顶点u,k表示已经走过了k个点,l表示所经过的路径和。
将贪心法的解作为上界的初始值。
get_lb(nodep)
求对应节点p的目标函数。
main()
主函数。
get_up()
求分支限界法的上界。
get_low()
求分支界限法的下界。
solve()
利用分支限界法求解函数
首先通过贪心法求解的值作为上界,把每个点最近的两条边之和的1/2作为下界。
分支限界法通过设定目标函数,每次从优先级队列中取目标函数的值最小的节点。
先判断是否已经经过了n-1个点,如果已经经过了n-1个点,那么可直接求出最短路径和,并与在队列里的其他节点的目标函数值比较,如果该路径和比其他所有在队列里的节点的目标函数值都小,那么改路径和就是问题的解。
否则,继续计算优先级队列里面的其他节点。
3、源程序及注释:
这里默认顶点的个数小于22。
1、动态规划法
#include<
iostream>
cstdio>
#defineINF9999
usingnamespacestd;
intmp[22][22];
intn;
voidin()
scanf("
%d"
&
n);
for(inti=0;
i<
n;
i++)
for(intj=0;
j<
j++)
if(i==j)
mp[i][j]=INF;
continue;
mp[i][j]);
}
intdp[22][1<
22];
intsolve()
ints=(1<
(n-1));
dp[0][0]=0;
for(inti=1;
dp[i][0]=mp[i][0];
dp[0][(s-1)]=INF;
for(intj=1;
(s-1);
j++)//总共有n-1个点,但不能全部取
i++)//把1~(n-1)这n-1个点,映射为集合对应的二进制数中的0~(n-2)位
if((j&
(1<
(i-1)))==0)//i不在集合中
intm=INF;
for(intk=1;
k<
k++)
(k-1)))>
0)//k在集合中
inttmp=dp[k][(j-(1<
(k-1)))]+mp[i][k];
if(m>
tmp)
m=tmp;
dp[i][j]=m;
dp[0][s-1]=INF;
dp[0][s-1]=min(dp[0][s-1],mp[0][i]+dp[i][(s-1)-(1<
(i-1))]);
returndp[0][s-1];
intmain()
in();
printf("
%d\n"
solve());
return0;
2、贪心法
intinq[22];
i<
=n;
i++)
j<
j++)
intdfs(intu,intk,intl)
if(k==n)returnl+mp[u][1];
intminlen=INF,p;
if(inq[i]==0&
&
minlen>
mp[u][i])/*取与所有点的连边中最小的边*/
minlen=mp[u][i];
p=i;
inq[p]=1;
returndfs(p,k+1,l+minlen);
inq[1]=1;
dfs(1,1,0));
3、分支限界法
//分支限界法
algorithm>
queue>
#defineINF100000
/*n*n的一个矩阵*/
//最少3个点,最多15个点
/*输入距离矩阵*/
intst_p;
//起点的邻接点
inted_p;
//终点的邻接点
intlow,up;
//确定上界
intget_lb(nodep)
intret=p.sumv*2;
//路径上的点的距离
intmin1=INF,min2=INF;
//起点和终点连出来的边
if(p.visp[i]==0&
min1>
mp[i][p.st])
min1=mp[i][p.st];
ret+=min1;
min2>
mp[p.ed][i])
min2=mp[p.ed][i];
ret+=min2;
if(p.visp[i]==0)
min1=min2=INF;
if(min1>
mp[i][j])
min1=mp[i][j];
if(min2>
mp[j][i])
min2=mp[j][i];
ret+=min1+min2;
returnret%2==0?
(ret/2):
(ret/2+1);
voidget_up()
up=dfs(1,1,0);
voidget_low()
low=0;
/*通过排序求两个最小值*/
inttmpA[22];
tmpA[j]=mp[i][j];
sort(tmpA+1,tmpA+1+n);
//对临时的数组进行排序
low+=tmpA[1];
/*贪心法确定上界*/
get_up();
/*取每行最小的边之和作为下界*/
get_low();
/*设置初始点,默认从1开始*/
nodestar;
star.st=1;
star.ed=1;
star.k=1;
i++)star.visp[i]=0;
star.visp[1]=1;
star.sumv=0;
star.lb=low;
/*ret为问题的解*/
intret=INF;
q.push(star);
while(!
q.empty())
nodetmp=q.top();
q.pop();
if(tmp.k==n-1)
/*找最后一个没有走的点*/
intp;
if(tmp.visp[i]==0)
break;
intans=tmp.sumv+mp[p][tmp.st]+mp[tmp.ed][p];
nodejudge=q.top();
/*如果当前的路径和比所有的目标函数值都小则跳出*/
if(ans<
=judge.lb)
ret=min(ans,ret);
/*否则继续求其他可能的路径和,并更新上界*/
else
up=min(up,ans);
ret=min(ret,ans);
/*当前点可以向下扩展的点入优先级队列*/
nodenext;
next.st=tmp.st;
/*更新路径和*/
next.sumv=tmp.sumv+mp[tmp.ed][i];
/*更新最后一个点*/
next.ed=i;
/*更新顶点数*/
next.k=tmp.k+1;
/*更新经过的顶点*/
j++)next.visp[j]=tmp.visp[j];
next.visp[i]=1;
/*求目标函数*/
next.lb=get_lb(next);
/*如果大于上界就不加入队列*/
if(next.lb>
up)continue;
q.push(next);
returnret;
四、运行输出结果:
(贴出程序运行完成时的屏幕截图或者输出文件的内容)
这里采用相同的两组数据进行测试。
样例1:
样例2:
2、贪心法
贪心法只能求局部最优解,局部最优解不一定是全局最优解。
五、调试和运行程序过程中产生的问题及采取的措施:
1、动态规划法中输出错误,通过测试数据进行反复验证,并分块输出局部结果,从而发现问题并解决。
2、贪心法对第二组样例的解不正确,因为局部最优解不一定是全局最优解。
3、分支限界法对于测试样例输出随机值,solve()函数在每次返回的时候结果不一致。
通过反复观察代码,发现循环跳出的条件有问题,应该是当前的解小于或等于队列中的目标函数值才跳出。
六、对算法的程序的讨论、分析,改进设想,其它经验教训:
1、动态规划法算法时间复杂度为O(
),在oj上的测试时间如下:
(oj上的测试样例n最大值为15)
2、贪心法只能求局部最优解,不一定是全局最优解。
所以第二组样例的解不正确。
3、分支限界法的复杂度是根据数据的不同而不同,搜索的节点越少,复杂度越低,跟目标函数的选择有很大关系。
目标函数值的计算也会需要一定时间,比如此文章中的目标函数值求解的复杂度是O(
)。
在oj上的测试时间如下:
在设置节点的时候,用数组标记经过的顶点,visp[i]=1,则说明i点已经经过了,由于是静态分配空间,所以每次创建新的节点,都会增加空间。
所以可以考虑动态分配空间,把不用的节点的空间释放掉。
4、对于顶点少的TSP问题,还可以采用蛮力法。
时间复杂度为O(n!
),但实现起来比较简单,这里使用了stl中生成全全排列的函数next_permutation()。
(测试时限为5000ms)
下面给出蛮力法的代码:
intmp[22][22],n;
#defineINF99999
inta[22],b[22],c[22];
intret=99999;
a[i]=i;
b[i]=i;
c[i]=i;
do
inttmp[22];
tmp[i]=b[a[i]];
intsum=0;
for(inti=2;
sum+=mp[tmp[i-1]][tmp[i]];
sum+=mp[tmp[n]][tmp[1]];
if(sum<
ret)
ret=sum;
i++)c[i]=tmp[i];
}while(next_permutation(a+1,a+1+n));