54.
55.for(int i=0;i<=N;i++)
56. {
57.delete []a[i];
58. }
59.delete []a;
60.
61. a=0;
62.return 0;
63.}
64.
65.template
66.void Traveling::Backtrack(int i)
67.{
68.if (i == n)
69. {
70.if (a[x[n-1]][x[n]] != 0 && a[x[n]][1] != 0 &&
71. (cc + a[x[n-1]][x[n]] + a[x[n]][1] < bestc || bestc == 0))
72. {
73.for (int j = 1; j <= n; j++) bestx[j] = x[j];
74. bestc = cc + a[x[n-1]][x[n]] + a[x[n]][1];
75. }
76. }
77.else
78. {
79.for (int j = i; j <= n; j++)
80. {
81.// 是否可进入x[j]子树?
82.if (a[x[i-1]][x[j]] != 0 && (cc + a[x[i-1]][x[i]] < bestc || be
stc == 0))
83. {
84.// 搜索子树
85. Swap(x[i], x[j]);
86. cc += a[x[i-1]][x[i]]; //当前费用累加
87. Backtrack(i+1); //排列向右扩展,排列树向下一层扩展
88. cc -= a[x[i-1]][x[i]];
89. Swap(x[i], x[j]);
90. }
91. }
92. }
93.}
94.
95.template
96.Type TSP(Type **a, int n)
97.{
98. Traveling Y;
99. Y.n=n;
100. Y.x=new int[n+1];
101. Y.bestx=new int[n+1];
102.
103.for(int i=1;i<=n;i++)
104. {
105. Y.x[i]=i;
106. }
107.
108. Y.a=a;
109. https://www.360docs.net/doc/2317683989.html,=0;
110. Y.bestc=0;
111.
112. Y.NoEdge=0;
113. Y.Backtrack(2);
114.
115. cout<<"最短回路为:"<116.for(int i=1;i<=n;i++)
117. {
118. cout< ";
119. }
120. cout<121.
122.delete [] Y.x;
123. Y.x=0;
124.delete [] Y.bestx;
125.
126. Y.bestx=0;
127.return Y.bestc;
128.}
129.
130.template
131.inline void Swap(Type &a, Type &b)
132.{
133. Type temp=a;
134. a=b;
135. b=temp;
136.}
算法backtrack在最坏情况下可能需要更新当前最优解O((n-1)!)次,每次更新bestx需计算时间O(n),从而整个算法的计算时间复杂性为
O(n!)。
程序运行结果如图:
2、圆排列问题
问题描述
给定n个大小不等的圆c1,c2,…,cn,现要将这n个圆排进一个矩形框中,且要求各圆与矩形框的底边相切。圆排列问题要求从n个圆的所有排列中找出有最小长度的圆排列。例如,当n=3,且所给的3个圆的半径分别为1,1,2时,这3个圆的最小长度的圆排列如图所示。其最小长度为。
问题分析
圆排列问题的解空间是一棵排列树。按照回溯法搜索排列树的算法框架,设开始时a=[r1,r2,……rn]是所给的n个元的半径,则相应的排列树由a[1:n]的所有排列构成。
解圆排列问题的回溯算法中,CirclePerm(n,a)返回找到的最小的圆排列长度。初始时,数组a是输入的n个圆的半径,计算结束后返回相应于最优解的圆排列。center计算圆在当前圆排列中的横坐标,由x^2 =
sqrt((r1+r2)^2-(r1-r2)^2)推导出x = 2*sqrt(r1*r2)。Compoute计算当前圆排列的
长度。变量min记录当前最小圆排列长度。数组r表示当前圆排列。数组x则记录当前圆排列中各圆的圆心横坐标。
在递归算法Backtrack中,当i>n时,算法搜索至叶节点,得到新的圆排列方案。此时算法调用Compute计算当前圆排列的长度,适时更新当前最优值。
当i算法具体代码如下:
[cpp]view plain copy
1.//圆排列问题回溯法求解
2.#include "stdafx.h"
3.#include
4.#include
https://www.360docs.net/doc/2317683989.html,ing namespace std;
6.
7.float CirclePerm(int n,float *a);
8.
9.template
10.inline void Swap(Type &a, Type &b);
11.
12.int main()
13.{
14.float *a = new float[4];
15. a[1] = 1,a[2] = 1,a[3] = 2;
16. cout<<"圆排列中各圆的半径分别为:"<17.for(int i=1; i<4; i++)
18. {
19. cout<20. }
21. cout<22. cout<<"最小圆排列长度为:";
23. cout<24.return 0;
25.}
26.
27.class Circle
28.{
29.friend float CirclePerm(int,float *);
30.private:
31.float Center(int t);//计算当前所选择的圆在当前圆排列中圆心的横坐标
32.void Compute();//计算当前圆排列的长度
33.void Backtrack(int t);
34.
35.float min, //当前最优值
36. *x, //当前圆排列圆心横坐标
37. *r; //当前圆排列
38.int n; //圆排列中圆的个数
39.};
40.
41.// 计算当前所选择圆的圆心横坐标
42.float Circle::Center(int t)
43.{
44.float temp=0;
45.for (int j=1;j46. {
47.//由x^2 = sqrt((r1+r2)^2-(r1-r2)^2)推导而来
48.float valuex=x[j]+2.0*sqrt(r[t]*r[j]);
49.if (valuex>temp)
50. {
51. temp=valuex;
52. }
53. }
54.return temp;
55.}
56.
57.// 计算当前圆排列的长度
58.void Circle::Compute(void)
59.{
60.float low=0,high=0;
61.for (int i=1;i<=n;i++)
62. {
63.if (x[i]-r[i]64. {
65. low=x[i]-r[i];
66. }
67.
68.if (x[i]+r[i]>high)
69. {
70. high=x[i]+r[i];
71. }
72. }
73.if (high-low74. {
75. min=high-low;
76. }
77.}
78.
79.void Circle::Backtrack(int t)
80.{
81.if (t>n)
82. {
83. Compute();
84. }
85.else
86. {
87.for (int j = t; j <= n; j++)
88. {
89. Swap(r[t], r[j]);
90.float centerx=Center(t);
91.if (centerx+r[t]+r[1]92. {
93. x[t]=centerx;
94. Backtrack(t+1);
95. }
96. Swap(r[t], r[j]);
97. }
98. }
99.}
100.
101.float CirclePerm(int n,float *a)
102.{
103. Circle X;
104. X.n = n;
105. X.r = a;
106. X.min = 100000;
107.float *x = new float[n+1];
108. X.x = x;
109. X.Backtrack(1);
110.delete []x;
111.return X.min;
112.}
113.
114.template
115.inline void Swap(Type &a, Type &b)
116.{
117. Type temp=a;
118. a=b;
119. b=temp;
120.}
如果不考虑计算当前圆排列中各圆的圆心横坐标和计算当前圆排列长度所需的计算时间按,则Backtrack需要O(n!)计算时间。由于算法Backtrack在最坏情况下需要计算O(n!)次圆排列长度,每次计算需要O(n)计算时间,从而整个算法的计算时间复杂性为O((n+1)!)上述算法尚有许多改进的余地。例如,像1,2,…,n-1,n和
n,n-1, …,2,1这种互为镜像的排列具有相同的圆排列长度,只计算一个就够了,可减少约一半的计算量。另一方面,如果所给的n个圆中有k 个圆有相同的半径,则这k个圆产生的k!个完全相同的圆排列,只计算一个就够了。
程序运行结果为: