编译环境:Dev-C++
运用回溯法和分支限界法解决最小重量机器设计问题,并分析时间复杂性。
算法描述及步骤
问题描述:
设某一机器由n个部件组成,每种部件都可以从m个不同的供应商处购得。设wij是从供应商j处购得的部件i的重置,cij是相应的价格。设计一个优先队列式分支限界法,给出总价格不超过d的最小重量机器设计。
算法设计:
对于给定的机器部件重量和机器部件价格,计算总价格不超过d的最小重量机器设计。
数据输入:
第1行有3个正整数n,m和d。接下来的2n行,每行n个数。前n行是c,后n行是w。
算法描述:
回溯法:
用回溯法求解问题时,应明确定义问题的解空间。问题的解空间至少应包含问题的一个(最优)解。该空间包含对变量的所有可能赋值即供应商与部件数组。
确定了解空间的组织结构后,回溯法从开始结点(根节点)出发,以深度优先方式搜索整个解空间。这个开始结点成为活结点,同时成为当前扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,即节点到叶子结点重量和大于当前最小重量或该节点到叶子节点价格和超过d时,则当前扩展结点就成为死结点。此时,应回溯至最近的一个活动结点处,并使这个活动结点成为当前的扩展结点。.
回溯法以这种工作方式递归地在解空间中搜索,直至找到所要求的解或者解空间中已无活动结点为止,遍历完之后的最新最小重量即为最优值。
分支限界法:
最小机器重量设计问题的优先队列式分支限界法用优先队列存储活结点表。
优先队列中的优先级定义为:活结点m对应的子树最小重量bestweight = node.weight + weightlist[t][i]。weight为该节点重量,weightlist为重量数组列表。从根节点开始遍历,遍历该扩展节点的所有子节点,对于符合要求的子节点即插入优先队列中
其中剪枝函数为bestweight < Bestweight且bestprice <= D。限界函数即该节点到叶子结点重量和小于当前最小重量;约束函数即该节点到叶子节点价格和不超过d。最小值和遍历完之后成为死节点。此时从优先队列中将当先优先级最高即重量最小的节点向下遍历,当遍历到叶子节点,若当前节点重量小于目前最小重量即更新最优值。
回溯法的求解目标是找出解空间中满足约束条件的所有解,以深度优先的方式搜索解空间树,当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。
分支限界法的求解目标则是找出解空间中满足约束条件的一个解,以广度优先或以最小耗度优先的方式搜索解空间树。一个活结点只有一次机会成为扩展结点,一旦成为扩展结点,就一次性产生所有儿子结点,并舍弃不可行解或非最优解的子结点。其余儿子结点加入活结点表。此后,从活动结点表中取出下一结点成为当前扩展结点,并重复以上过程。
调试过程及结果
在开始时,最小堆是打算用链表来实现,但在具体实践时出现了很多问题,所以将最小堆改用优先队列来实现,这样每次加入的活结点能够根据设定的优先级进行自动排序。
在分支限界法里,对优先级的判断中,当两个结点计算出的可能的最小重量相同时,应该设定层次更深的结点优先级更高,在一开始时忽略了这一点,导致测试时出现结果错误等问题,把优先级的判定加上层次这一条件后,结果正确。
实验结果为:
回溯法:

分支限界法:

源代码:
回溯法
//回溯法解最小重量机器设计问题
#include<stdio.h>
#define maxn 110
int w[maxn][maxn];
int c[maxn][maxn];
int bestx[maxn];
int x[maxn];
int n, m, d;
int cw = 0, cc = 0, bestw = 0x3f3f3f3f;
void Backtrack(int t) {
if (t > n) {
bestw = cw;
for (int i = 1; i <= n; i++)
bestx[i] = x[i];
//return;
}else {
for (int i = 1; i <= m; i++) {
if (cc + c[t][i] <= d && cw + w[t][i] < bestw) {
x[t] = i;
cc += c[t][i];
cw += w[t][i];
Backtrack(t + 1);
cc -= c[t][i];
cw -= w[t][i];
}
}
}
}
int main() {
puts("请依次输入部件数,供应商数,限定价格:");
scanf("%d %d %d",&n,&m,&d);
puts("请输入各部件的在不同供应商的重量:");
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d",&w[i][j]);
puts("请输入各部件的在不同供应商的价格:");
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d",&c[i][j]);
Backtrack(1);
printf("最小重量为:%d\n",bestw);
puts("每个部件的供应商:");
for (int i = 1; i <= n; i++)
if(i==1) printf("%d",bestx[i]);
else printf(" %d",bestx[i]);
return 0;
}
分支界限法
//分支限界法解最小重量机器设计问题
#include <bits/stdc++.h>
using namespace std;
int n; //部件数量
int m; //供应商数量
int d; //价格上限
int bestw; //最小的重量
int** c = NULL; //二维数组,每个部件不同商家的价格
int** w = NULL; //二维数组,每个部件不同商家的重量
//每一个部件的信息
class Node {
public:
int weight; //当前已选机器的重量和
int val; //当前已选机器的价值和
int source; //哪个供货商
int level; //第几层,也代表了第几个部件
int priority; //优先级
Node* father;
};
Node* leaf;//叶子结点
void Input() {
puts("请依次输入部件数,供应商数,限定价格:");
scanf("%d %d %d",&n,&m,&d);
w = (int**)malloc(sizeof(int*)*(n + 1));
c = (int**)malloc(sizeof(int*)*(n + 1));
for (int i = 1; i <= n; i++) {
w[i] = (int*)malloc(sizeof(int)*(m + 1));
c[i] = (int*)malloc(sizeof(int)*(m + 1));
}
leaf = NULL;
puts("请输入各个部件在各个供应商处购买的价格:");
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d",&c[i][j]);
puts("请输入各个部件在各个供应商处购买的重量:");
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d",&w[i][j]);
}
//优化的优先级设定
bool operator<(Node a, Node b) //level按照减序
{
if (a.priority == b.priority)return a.level < b.level; //如果重量相同,选择level大的。
return a.priority > b.priority;//否则,重量小的先出队
}
//计算当前节点的优先级
void QueuePriority(Node a) {
int currentMinW;
a.priority = a.val;
//int temp_min_c = INT_MAX;
for (int i = a.level + 1; i <= n; i++)//选出剩余的部件在售货商中购买的最小质量,就是选择每一层最小的质量
{
currentMinW = 999999;
for (int j = 1; j <= m; j++) //每一层找最小的
{
currentMinW = currentMinW < w[i][j] ? currentMinW : w[i][j];//从m个商家中选择当层重量最小的
}
a.priority += currentMinW;
}
}
//约束函数
bool constraint(Node* pNode, int i) {
return pNode->val + c[pNode->level + 1][i] <= d || pNode->weight + w[pNode->level + 1][i] <= bestw;
}
//创建节点
Node createNode(int level, Node* father, int source, int val, int weight) {
Node newNode{};
newNode.level = level;//层次加1
newNode.father = father;
newNode.source = source;
newNode.val = val;
newNode.weight = weight;
return newNode;
}
void MinWeightMachine() {
int i, j;
bestw = 999999;
Node initial{};
initial = createNode(0, NULL, 0, 0, 0);
QueuePriority(initial); //计算优先级
priority_queue<Node> heap; //用优先队列,建立一个最小堆。加入进去就会自动排好序的。
heap.push(initial);
while (!heap.empty()) {
Node* pNode = new Node(heap.top());
heap.pop();//队首元素作为父节点出队,即优先级值小的活结点先扩展
if (pNode->level == n)//到达叶节点,不能扩展 ,得到一个解
{
if (pNode->weight < bestw) //更新
{
bestw = pNode->weight;
//MinValue = pNode ->val;
leaf = pNode; //记录是最后是哪个结点数据,便于回溯找最优解
}
}
else {
for (i = 1; i <= m; i++)//扩展结点,依次选择每个售货商,每次都是m叉树
{
//可行性剪枝和限界剪枝
if (constraint(pNode, i)) {
Node newNode{};//生儿子结点
newNode = createNode(pNode->level + 1, pNode, i, pNode->val + c[pNode->level + 1][i], pNode->weight + w[pNode->level + 1][i]);
QueuePriority(newNode); //计算优先值
heap.push(newNode);//儿子入队
}
}
}
}
}
void Output() {
printf("求得的最小重量为:%d\n",bestw);
int* result = (int*)malloc(sizeof(int)*(n + 1));
for (int i = n; i >= 1; i--) {
result[i] = leaf->source;//从最后叶子结点回溯到根节点
leaf = leaf->father;
}
puts("各个部件的供应商分别为:");
for (int i = 1; i <= n; i++)
if(i==1) printf("%d",result[i]);
else printf(" %d",result[i]);
putchar('\n');
}
int main() {
Input();
MinWeightMachine();
Output();
return 0;
}
&spm=1001.2101.3001.5002&articleId=127761037&d=1&t=3&u=c14c96f7321641c69e81827b52811659)
541

被折叠的 条评论
为什么被折叠?



