5.1 代码如下: 5.1代码如下: 5.1代码如下:
#include<iostream>
using namespace std;
int n=3;
int x[3]={1,1,1};
void show(int a[])
{
for(int i=0;i<n;i++)
cout<<a[i]<<"\t";
cout<<endl<<endl;
}
//约束条件
bool P(int x[],int i)//表示第几层
{
switch(i)
{
case 0:
if(3*x[0]<=12)return true;
else return false;
case 1:
if(3*x[0]+4*x[1]<=12)return true;
else return false;
case 2:
if(3*x[0]+4*x[1]+2x[2]<=12)return true;
else return false;
}
}
void fun(int i)
{
// cout<<i<<"c"<<endl;
// show(x);
if(i>n-1)//已经结束了最后一个数
{
// cout<<"结果:";
show(x);
return;//结束最后一层,回溯到上一层
}
//若不变
if(P(x,i)&&x[i]<4)//符合约束条件
{
x[i]=1;
fun(i+1); //下一层
}
//若2
if(P(x,i)&&x[i]<4)//符合约束条件
{
int t=x[i];
x[i]=2;
fun(i+1); //下一层
x[i]=t;//回复
}
//若3
if(P(x,i)&&x[i]<4)//符合约束条件
{
int t=x[i];
x[i]=3;
fun(i+1); //下一层
x[i]=t;//回溯后,本层尝试下一个可能
}
}
int main()
{
fun(0);
return 0;
}
解分别为:
(
0
,
0
,
0
)
,
(
0
,
0
,
1
)
,
(
0
,
0
,
2
)
,
(
0
,
0
,
3
)
,
(
0
,
0
,
4
)
,
(
0
,
0
,
5
)
,
(
0
,
0
,
6
)
,
解分别为:(0,0,0), (0,0,1), (0,0,2), (0,0,3), (0,0,4), (0,0,5), (0,0,6),
解分别为:(0,0,0),(0,0,1),(0,0,2),(0,0,3),(0,0,4),(0,0,5),(0,0,6),
(
0
,
1
,
0
)
,
(
0
,
1
,
1
)
,
(
0
,
1
,
2
)
,
(
0
,
1
,
3
)
,
(
0
,
1
,
4
)
,
(
0
,
2
,
0
)
,
(
0
,
2
,
1
)
,
(0,1,0), (0,1,1), (0,1,2), (0,1,3), (0,1,4), (0,2,0), (0,2,1),
(0,1,0),(0,1,1),(0,1,2),(0,1,3),(0,1,4),(0,2,0),(0,2,1),
(
0
,
2
,
2
)
,
(
0
,
3
,
0
)
,
(
1
,
0
,
0
)
,
(
1
,
0
,
1
)
,
(
1
,
0
,
2
)
,
(
1
,
0
,
3
)
,
(
1
,
0
,
4
)
,
(0,2,2), (0,3,0), (1,0,0), (1,0,1), (1,0,2), (1,0,3), (1,0,4),
(0,2,2),(0,3,0),(1,0,0),(1,0,1),(1,0,2),(1,0,3),(1,0,4),
(
1
,
1
,
0
)
,
(
1
,
1
,
1
)
,
(
1
,
1
,
2
)
,
(
1
,
2
,
0
)
,
(
2
,
0
,
0
)
,
(
2
,
0
,
1
)
,
(
2
,
0
,
2
)
,
(1,1,0), (1,1,1), (1,1,2), (1,2,0), (2,0,0), (2,0,1), (2,0,2),
(1,1,0),(1,1,1),(1,1,2),(1,2,0),(2,0,0),(2,0,1),(2,0,2),
(
2
,
0
,
3
)
,
(
2
,
1
,
0
)
,
(
2
,
1
,
1
)
,
(
3
,
0
,
0
)
,
(
3
,
0
,
1
)
,
(
4
,
0
,
0
)
(2,0,3), (2,1,0), (2,1,1), (3,0,0), (3,0,1), (4,0,0)
(2,0,3),(2,1,0),(2,1,1),(3,0,0),(3,0,1),(4,0,0)
5.2 5.2 5.2
#include <iostream>
#include <fstream>
using namespace std;
class Machine
{
public:
int n_, m_, d_;// n_零件个数,m_供应商个数,d_最大总价格
int** c_; // c_[i][j]第i个零件第j个供应商得价格
int** w_; //w_[i][j]第i个零件第j个供应商的重量
int sumw = 0;//初始化当前已选择的零件的重量
int sumc = 0;//当前已选择的零件的价格
int* path;//记录第i个零件选择的供应商
int minw = INT_MAX; //初始化最小重量
int *bestpath;//取得最小重量时各供应商得选择
Machine() // 把初始数据从文件中读出并初始化类
{
ifstream infile("input.txt");
if (!infile)
{
cout << "open input.txt error!!\n";
return;
}
infile >> n_ >> m_ >> d_;
c_ = new int* [n_];
w_ = new int* [n_];
path = new int[n_];
bestpath = new int[n_];
for (int i = 0; i < n_; i++)
{
c_[i] = new int[m_];
w_[i] = new int[m_];
}
for (int i = 0; i < n_; i++)
{
for (int j = 0; j < m_; j++)
{
infile >> c_[i][j];
}
}
for (int i = 0; i < n_; i++)
{
for (int j = 0; j < m_; j++)
{
infile >> w_[i][j];
}
}
infile.close();
}
~Machine() //析构函数,释放内存
{
for (int i = 0; i < n_; i++)
{
delete[] c_[i];
delete[] w_[i];
}
delete c_;
delete w_;
}
void Backtracking(int i) //主回溯函数
{
if (i >= n_) //递归终止条件
{
//重量小于最小重量,更新
if (sumw < minw)
{
for (int i = 0; i < n_; i++)
{
bestpath[i] = path[i];
}
minw = sumw;
}
return;
}
else
{
for (int j = 0; j < m_; j++) //遍历m个供应商
{
sumw += w_[i][j];
sumc += c_[i][j];
path[i] = j;
if (sumw < minw && sumc <= d_)//当前总价格小于最大价格,当前总重量小于最小总重量
{
Backtracking(i + 1);
}
//回溯
sumw -= w_[i][j];
sumc -= c_[i][j];
}
}
}
void WriteFile()//把结果写入文件
{
ofstream out("output.txt");
out << minw << endl;
for (int i = 0; i < n_; i++)
{
out << bestpath[i] + 1 << " ";
}
out.close();
}
};
int main()
{
Machine x;
x.Backtracking(0);
x.WriteFile();
return 0;
}
运行上面程序,这 4 种配件应分别选择供货商 3 , 1 , 2 , 3 , 总重量为 31 , 价值为 119. 运行上面程序,这4种配件应分别选择供货商3,1,2,3,总重量为31,价值为119. 运行上面程序,这4种配件应分别选择供货商3,1,2,3,总重量为31,价值为119.
5.3
所有可能的
L
a
t
i
n
方有
24
个,如下所示
:
5.3所有可能的Latin方有24个,如下所示:
5.3所有可能的Latin方有24个,如下所示:
5.6
设
S
=
{
a
1
,
a
2
,
…
,
a
n
}
.
求
S
满足条件
∑
a
i
∈
A
a
i
=
M
的所有的子集
A
.
5.6设S=\lbrace a_{1},a_{2},…,a_{n}\rbrace.求S 满足条件\sum_{a_{i}\in A} a_{i}=M 的所有的子集A.
5.6设S={a1,a2,…,an}.求S满足条件∑ai∈Aai=M的所有的子集A.
用回溯算法
.
解向量为
<
x
1
,
x
2
,
…
,
x
n
>
,
x
i
=
0
,
1.
其中
x
i
=
1
当且仅当
a
i
∈
A
.
用回溯算法.解向量为<x_{1},x_{2},…,x_{n} >,x_{i}=0,1.其中x_{i}=1当且仅当a_{i}\in A.
用回溯算法.解向量为<x1,x2,…,xn>,xi=0,1.其中xi=1当且仅当ai∈A.
搜索空间为子集树
.
搜索空间为子集树.
搜索空间为子集树.
部分向量
<
x
1
,
x
2
,
…
,
x
k
>
表示已经考虑了对
a
1
,
a
2
,
…
,
a
k
的选择
.
部分向量<x_{1},x_{2},…,x_{k}>表示已经考虑了对a_{1},a_{2},…,a_{k} 的选择.
部分向量<x1,x2,…,xk>表示已经考虑了对a1,a2,…,ak的选择.
结点分支的约束条件是
:
B
(
i
)
=
∑
i
=
1
k
a
i
x
i
<
M
且
a
k
+
1
∈
S
−
{
a
1
,
a
2
,
.
.
.
,
a
k
}
结点分支的约束条件是:B(i)=\sum_{i=1}^{k}a_{i}x_{i}<M 且a_{k+1}\in S-\lbrace a_{1},a_{2},...,a_{k}\rbrace
结点分支的约束条件是:B(i)=∑i=1kaixi<M且ak+1∈S−{a1,a2,...,ak}
最坏情况下算法的时间复杂度是
O
(
2
n
)
.
最坏情况下算法的时间复杂度是O(2^{n}).
最坏情况下算法的时间复杂度是O(2n).
5.7
设
n
个人的集合是
{
1
,
2
,
…
,
n
}
,
n
项工作的集合是
{
1
,
2
,
…
,
n
}
,
每个人恰好
1
项工作
.
5.7设n 个人的集合是\lbrace 1,2,…,n\rbrace,n 项工作的集合是\lbrace 1,2,…,n\rbrace,每个人恰好1项工作.
5.7设n个人的集合是{1,2,…,n},n项工作的集合是{1,2,…,n},每个人恰好1项工作.
x
i
=
j
表示把工作
j
分配给
i
,
其中
i
,
j
=
1
,
2
,
…
,
n
x_{i}=j表示把工作j分配给i,其中i,j=1,2,…,n
xi=j表示把工作j分配给i,其中i,j=1,2,…,n
解向量是
X
=
<
x
1
,
x
2
,
…
,
x
n
>
,
分配成本是
C
(
X
)
=
∑
i
=
1
k
C
(
i
,
x
i
)
解向量是X=<x_{1},x_{2},…,x_{n}>,分配成本是C(X)=\sum_{i=1}^{k}C(i,x_{i})
解向量是X=<x1,x2,…,xn>,分配成本是C(X)=∑i=1kC(i,xi)
搜索空间是排列树
.
搜索空间是排列树.
搜索空间是排列树.
部分向量
<
x
1
,
x
2
,
…
,
x
k
>
表示已经考虑了对人
1
,
2
,
…
,
k
的工作分配
部分向量<x_{1},x_{2},…,x_{k}>表示已经考虑了对人1,2,…,k 的工作分配
部分向量<x1,x2,…,xk>表示已经考虑了对人1,2,…,k的工作分配
结点分支的约束条件是
:
x
k
+
1
∈
{
1
,
2
,
.
.
.
,
n
}
−
{
x
1
,
x
2
,
.
.
.
,
x
n
}
结点分支的约束条件是:x_{k+1}\in \lbrace 1,2,...,n\rbrace-\lbrace x_{1},x_{2},...,x_{n}\rbrace
结点分支的约束条件是:xk+1∈{1,2,...,n}−{x1,x2,...,xn}
代价函数:
F
(
x
1
,
x
2
,
.
.
.
,
x
k
)
=
∑
i
=
1
k
C
(
i
,
x
i
)
+
∑
i
=
k
+
1
n
m
i
n
(
C
(
i
,
t
)
∣
t
∈
{
1
,
2
,
…
,
n
}
−
{
x
1
,
x
2
,
.
.
.
,
x
n
}
)
代价函数:F(x_{1},x_{2},...,x_{k})=\sum_{i=1}^{k}C(i,x_{i})+\sum_{i=k+1}^{n}min(C(i,t)|t\in \lbrace 1,2,…,n\rbrace-\lbrace x_{1},x_{2},...,x_{n}\rbrace)
代价函数:F(x1,x2,...,xk)=∑i=1kC(i,xi)+∑i=k+1nmin(C(i,t)∣t∈{1,2,…,n}−{x1,x2,...,xn})
界
B
是已得到的最好可行解的分配成本
.
如果代价函数大于界
,
则回溯
.
界B 是已得到的最好可行解的分配成本.如果代价函数大于界,则回溯.
界B是已得到的最好可行解的分配成本.如果代价函数大于界,则回溯.
算法最坏情况下的时间复杂度是
O
(
n
n
!
)
.
算法最坏情况下的时间复杂度是O(nn!).
算法最坏情况下的时间复杂度是O(nn!).
5.9
设
n
个任务的标号分别为
1
,
2
,
…
,
n
,
k
个处理器的标号分别为
1
,
2
,
…
,
k
.
5.9设n 个任务的标号分别为1,2,…,n,k 个处理器的标号分别为1,2,…,k.
5.9设n个任务的标号分别为1,2,…,n,k个处理器的标号分别为1,2,…,k.
算法主要步骤如下
:
算法主要步骤如下:
算法主要步骤如下:
1.
给出将
n
个任务分到
k
个处理器上的分配方法
.
1.给出将n 个任务分到k 个处理器上的分配方法.
1.给出将n个任务分到k个处理器上的分配方法.
2.
在每个分配方法下
,
算出每个处理器上所分配到的任务的执行时间
,
并求这些执行时间的最大值
,
该最大值即为这个分配方法的执行时间
.
2.在每个分配方法下,算出每个处理器上所分配到的任务的执行时间,并求这些执行时间的最大值,该最大值即为这个分配方法的执行时间.
2.在每个分配方法下,算出每个处理器上所分配到的任务的执行时间,并求这些执行时间的最大值,该最大值即为这个分配方法的执行时间.
3.
求出所有分配方法的执行时间的最小值
.
3.求出所有分配方法的执行时间的最小值.
3.求出所有分配方法的执行时间的最小值.
步骤
1
可以用深度优先策略遍历的
k
元完全正则树实现
,
得到
k
n
个分配方案
.
步骤1可以用深度优先策略遍历的k元完全正则树实现,得到k^{n}个分配方案.
步骤1可以用深度优先策略遍历的k元完全正则树实现,得到kn个分配方案.
步骤
2
:
在分配方案
<
i
1
,
i
2
,
…
,
i
n
>
下
,
如
i
k
=
i
l
,
则表明任务
k
和
l
被分配到同一个处理器
i
k
上执行
.
步骤2:在分配方案<i_{1},i_{2},…,i_{n} >下,如i_{k}=i_{l},则表明任务k和l被分配到同一个处理器i_{k}上执行.
步骤2:在分配方案<i1,i2,…,in>下,如ik=il,则表明任务k和l被分配到同一个处理器ik上执行.
遍历序列
i
1
,
i
2
,
…
,
i
n
,
找到每个处理器上分配到的所有任务
w
1
,
w
2
,
…
,
w
u
,
则在这个分配方案下
,
遍历序列i_{1},i_{2},…,i_{n},找到每个处理器上分配到的所有任务 w_{1},w_{2},…,w_{u},则在这个分配方案下,
遍历序列i1,i2,…,in,找到每个处理器上分配到的所有任务w1,w2,…,wu,则在这个分配方案下,
该处理器的执行时间为
t
w
1
+
t
w
2
+
…
+
t
w
u
.
再在所有处理器的执行时间中求出最大值
,
该处理器的执行时间为t_{w_{1}} +t_{w_{2}} +…+t_{w_{u}} .再在所有处理器的执行时间中求出最大值,
该处理器的执行时间为tw1+tw2+…+twu.再在所有处理器的执行时间中求出最大值,
则该最大值即为分配方案
<
i
1
,
i
2
,
…
,
i
n
>
的执行时间
.
该步骤的执行时间是
O
(
n
k
n
)
.
则该最大值即为分配方案<i_{1},i_{2},…,i_{n} >的执行时间.该步骤的执行时间是O(nk^{n}).
则该最大值即为分配方案<i1,i2,…,in>的执行时间.该步骤的执行时间是O(nkn).
步骤
3
是在
k
n
个分配方案的执行时间中求最小值
,
执行时间是
O
(
k
n
)
.
步骤3是在kn 个分配方案的执行时间中求最小值,执行时间是O(kn ).
步骤3是在kn个分配方案的执行时间中求最小值,执行时间是O(kn).
所以,算法执行时间是
O
(
n
k
n
)
.
所以,算法执行时间是O(nk^{n} ).
所以,算法执行时间是O(nkn).
5.11
用
X
[
i
.
j
]
表示该
m
∗
n
的
0
−
1
矩阵
,
X
[
i
,
j
]
=
1
当且仅当陈列室
(
i
,
j
)
有哨兵
.
5.11用X[i.j]表示该m*n的0-1矩阵,X[i,j]=1当且仅当陈列室(i,j) 有哨兵.
5.11用X[i.j]表示该m∗n的0−1矩阵,X[i,j]=1当且仅当陈列室(i,j)有哨兵.
初始令所有的
X
[
i
,
j
]
=
1
,
i
=
1
,
2
,
…
,
m
,
j
=
1
,
2
,
…
,
n
.
初始令所有的 X[i,j]=1,i=1,2,…,m,j=1,2,…,n.
初始令所有的X[i,j]=1,i=1,2,…,m,j=1,2,…,n.
算法从
(
1
,
1
)
开始直到
(
m
,
n
)
为止
,
搜索树是二叉树
,
有
m
∗
n
层
.
算法从(1,1)开始直到(m,n)为止,搜索树是二叉树,有m*n层.
算法从(1,1)开始直到(m,n)为止,搜索树是二叉树,有m∗n层.
如果令
X
[
i
,
j
]
=
0
,
则取消
(
i
,
j
)
位置的哨兵
,
进入左子树
;
否则
,
进入右子树
.
如果令X[i,j]=0,则取消(i,j)位置的哨兵,进入左子树;否则,进入右子树.
如果令X[i,j]=0,则取消(i,j)位置的哨兵,进入左子树;否则,进入右子树.
在进入左子树时
,
需要检查房间被监视的情况
.
在进入左子树时,需要检查房间被监视的情况.
在进入左子树时,需要检查房间被监视的情况.
即当取消
(
i
,
j
)
位置的哨兵时
,
此位置及其上、下、左、右位置是否被监视
.
如果有不被监测的情况出现
,
则该分支不再搜索
.
即当取消(i,j)位置的哨兵时,此位置及其上、下、左、右位置是否被监视.如果有不被监测的情况出现,则该分支不再搜索.
即当取消(i,j)位置的哨兵时,此位置及其上、下、左、右位置是否被监视.如果有不被监测的情况出现,则该分支不再搜索.
最坏情况下的时间复杂度是
O
(
2
n
m
)
.
最坏情况下的时间复杂度是O(2^{nm}).
最坏情况下的时间复杂度是O(2nm).
X[i,j]=1,i=1,2,…,m,j=1,2,…,n.$
算法从
(
1
,
1
)
开始直到
(
m
,
n
)
为止
,
搜索树是二叉树
,
有
m
∗
n
层
.
算法从(1,1)开始直到(m,n)为止,搜索树是二叉树,有m*n层.
算法从(1,1)开始直到(m,n)为止,搜索树是二叉树,有m∗n层.
如果令
X
[
i
,
j
]
=
0
,
则取消
(
i
,
j
)
位置的哨兵
,
进入左子树
;
否则
,
进入右子树
.
如果令X[i,j]=0,则取消(i,j)位置的哨兵,进入左子树;否则,进入右子树.
如果令X[i,j]=0,则取消(i,j)位置的哨兵,进入左子树;否则,进入右子树.
在进入左子树时
,
需要检查房间被监视的情况
.
在进入左子树时,需要检查房间被监视的情况.
在进入左子树时,需要检查房间被监视的情况.
即当取消
(
i
,
j
)
位置的哨兵时
,
此位置及其上、下、左、右位置是否被监视
.
如果有不被监测的情况出现
,
则该分支不再搜索
.
即当取消(i,j)位置的哨兵时,此位置及其上、下、左、右位置是否被监视.如果有不被监测的情况出现,则该分支不再搜索.
即当取消(i,j)位置的哨兵时,此位置及其上、下、左、右位置是否被监视.如果有不被监测的情况出现,则该分支不再搜索.
最坏情况下的时间复杂度是
O
(
2
n
m
)
.
最坏情况下的时间复杂度是O(2^{nm}).
最坏情况下的时间复杂度是O(2nm).