最小重量机器设计问题(回溯-分支限界)

一、题目

★问题描述:设某一机器由 n n n个部件组成,每一种部件都可以从 m m m个不同的供应商处购得。设 w ij w_{\text{ij}} wij是从供应商 j j j处购得的部件 i i i的重量, c ij c_{\text{ij}} cij是相应的价格。

试设计一个算法,给出总价格不超过 c c c的最小重量机器设计。

★算法设计:对于给定的机器部件重量和机器部件价格,计算总价格不超过 d d d的最小机器重量设计。

★数据输入:由文件input.txt给出输入数据。第一行有3个正整数 n , m , d n,m,d n,m,d。接下来的2 n n n行,每行 n n n个数。前 n n n行是 c c c,后 n n n行是 w w w

★结果输出:将计算的最小重量及每个部件的供应商输出到文件output.txt。

输入文件示例 输出文件示例

input.txt
3 3 4
1 2 3
3 2 1
2 2 2
1 2 3
3 2 1
2 2 2

output.txt
4
1 3 1

二、题目解析

1、设计分析

题目是一个组合优化问题,解决此类问题可以考虑使用贪心算法、动态规划、回溯与分支限界算法。对于本题,如果使用贪心算法,每种部件都选择重量最小的供应商,显然会因为没有考虑价格而导致无法正确求解。另外,由于本题不符合优化原则即当选择 n n n种部件时的最优解不一定是选择 n + 1 n + 1 n+1种部件时的最优解,所以不能使用动态规划算法。由于本题符合多米诺性质即如果某一选择方案选择了 n n n个部件时不符合约束条件,那么无论第 n + k ( k > 0 ) n + k(k > 0) n+k(k>0)种部件如何选择,该选择方案都不符合约束条件,所以可以使用回溯算法,同时为了加快回溯速度,以下解法采用了分支限界技术。

解题过程: n n n种部件, m m m个供应商,总价格不超过 d d d,设每个供应商的编号分别为 1 , 2 , 3 ⋅ ⋅ ⋅ , m 1,2,3 \cdot \cdot \cdot ,m 123m

搜索空间为 { < x 1 , x 2 , x 3 , ⋅ ⋅ ⋅ x n >   ∣   x i ∈ N , 1 ≤ x i ≤ m } \left\{ < x_{1},x_{2},x_{3}, \cdot \cdot \cdot x_{n} > \ \right|\ x_{i} \in N,1 \leq x_{i} \leq m\} {<x1,x2,x3,xn>  xiN,1xim} x i x_{i} xi的值表示第 i i i个部件的供应商编号。

约束条件:所有部件的总价格 ≤ d \leq d d.

界: 当前已经得出的所有可行解对应的重量的最小值

代价函数: 当前已经选择的部件总重量 + + +剩余部件(不考虑价格)可能的最小重量即结

< x 1 , x 2 , x 3 , ⋅ ⋅ ⋅ , x k > < x_{1},x_{2},x_{3}, \cdot \cdot \cdot ,x_{k} > <x1,x2,x3,,xk>处代价函数的值为

F k = ∑ i = 1 k − 1 c i x i + ∑ i = k n w i ′ F_{k} = \sum_{i = 1}^{k - 1}c_{ix_{i}} + \sum_{i = k}^{n}w_{i}^{'} Fk=i=1k1cixi+i=knwi

其中 c ij c_{\text{ij}} cij为第 i i i个部件从第 j j j个供应商处购买的价格, w i ′ w_{i}^{'} wi为第 i i i个部件的最低价格

搜索策略:深度优先

在根结点处,没有选择任何部件,总重量和总价格都为 0 0 0,代价函数的值为所有部件的最低价格之和,界函数的初值为无穷大,代价函数小于界函数,所以往下搜索根结点的 m m m个分支。对于所有结点,都进行如下操作:

①判断当前结点代价函数的值是否大于界函数,如果是,则回溯。

②(分支限界)判断是否满足约束条件,如果不满足则回溯。

③如果在①和②处都没有回溯,则继续向下搜索 m m m个分支

如果搜索到叶子结点,即得到一个可行解,则将其与界函数比较,如果可行解对应的总重量小于界函数,则将界函数的值更新为该可行解对于的总重量,记录对于的供应商选择方案,然后回溯。

实例:
在这里插入图片描述
源代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
/*
n:部件数量
m:供应商数量
d:最大总价格
使用分支限界算法
	搜索策略:深度优先
	搜索树:树中的结点可表示为序列<X1,X2,X3···,Xn>,其
中Xi表示第i个部件选择的供应商编号(1<=Xi<=m)
	约束条件:总价格小于d
	界:当前已经求出的总价格<=d的最小重量和
	代价函数:当前已经选择的部件总重量+剩余部件(不考虑价格)可能的最小重量
*/
#define MAX 100
int cost[MAX][MAX],weight[MAX][MAX],minSum[MAX];
int result[MAX], tempResult[MAX];
int n, m, d, minWeight = 99999, curWeight = 0, curCost = 0;
/*
cost[i][j]:第i个部件从第j个供应商购买的价格
weight[i][j]:第i个部件从第j个供应商购买的重量
minSum[i]:不考虑价格,后(n-i)个部件的最小重量和

result[i]:储存第i个部件的供应商编号
tempResult[i]:在求解过程中储存第i个部件的供应商编号

n:部件的总数
m:供应商数量
minWeight:最小重量(目标函数)
curWeight:记录当前(已选部件)的总重量
curCost:记录当前(已选部件)的总价格
*/
void setMinSum()//计算minSum数组的值
{
	//minSum[i]:不考虑价格,后(n - i)个部件的最小重量和
	
	for (int i = n; i >=1; i--)
	{
		//后(n - i)个部件的最小重量和=后(n - i-1)个部件的最小重量和+第(n-i)个部件的最小重量
		minSum[i]=minSum[i+1]+ *(min_element(weight[i]+1, weight[i] + 1+m));
		/*printf("w%d:%d\n ",i, *(weight[i] + 3));
		printf("min:%d\n ", *(min_element(weight[i] + 1, weight[i] + m)));
		printf("sum:%d \n", minSum[i]);*/
	}
}

void Reback(int k)//求价格<=d的第k~n个部件最小重量和
{
	/*
	从搜索树的第k层开始搜索
	*/
	if (k == 1)
	{
		/*
		从第1层搜索时先初始化MinSum函数,便于
		之后计算代价函数
		*/
		setMinSum();
	}
	if (k > n)//到达叶子结点(所有部件都已选择完毕)
	{
		if (curWeight < minWeight)//当前的重量和<已经求出的的最小重量和
		{
			minWeight = curWeight;//更新最小重量和
			for (int i = 1; i <= n; i++)
			{
				result[i] = tempResult[i];//更新每个部件的供应商编号
			}
		}
		return;//到达叶子结点,回溯
	}
	else
	{
		if (minSum[k] + curWeight > minWeight)
		{
			
			/*
			代价函数:剩余部件的最小重量和+当前已选部件重量和
			界函数:当前已经求出的最小重量和

			当代价函数>界函数,直接回溯
			*/
			return;
		}
		for (int i = 1; i <= m; i++)//循环搜索选择第m个供应商的情况
		{
			if (curCost + cost[k][i] <= d)//如果总价格小于d
			{
				curWeight += weight[k][i];//当前总重量加上第k个部件从第i个供应商购买的重量
				curCost += cost[k][i];//当前总价格加上第k个部件从的第i个供应商购买的价格
				tempResult[k] = i;//记录第k个部件的供应商编号
				
				Reback(k + 1);//递归,求价格<=d的第k+1~n个部件最小重量搜索
				
				//在下一次循环前先将重量和价格恢复原状
				curWeight -= weight[k][i];
				curCost -= cost[k][i];

			}
		}



	}
}

int main()
{
	FILE* fr = fopen("./input.txt", "r");
	fscanf(fr, "%d%d%d", &n, &m, &d);//从input.txt中读取n,m,d
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			fscanf(fr,"%d", &cost[i][j]);//读取第i个部件从的j个供应商购买的价格
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			fscanf(fr,"%d", &weight[i][j]);//读取第i个部件从的j个供应商购买的重量
			//printf("%d%d :%d   ",i,j,weight[i][j]);
		}
	}
	fclose(fr);

	Reback(1);//求价格<=d的1~n个部件的最小重量和
	FILE* fw = fopen("./output.txt", "w");
	fprintf(fw, "%d\n", minWeight);//将最小重量和输出到output.txt
	for (int i = 1; i <= n; i++)
	{
		//将每个部件的供应商编号输出的output.txt
		fprintf(fw,"%d ", result[i]);
	}
	fclose(fw);

	//打印结果
	printf("最小重量: %d\n", minWeight);
	printf("各部件供应商:");
	for (int i = 1; i <= n; i++)
	{
		printf("%d ", result[i]);
	}
	printf("\n");

	return 0;
}

三、测试实例

input-1
8 18 14
18 15 20 5 15 10 16 6 1 6 17 6 1 2 17 15 13 17
16 6 7 4 7 2 11 6 18 4 13 12 8 5 2 8 15 14
12 6 19 10 13 8 2 10 16 4 15 15 16 13 17 12 14 4
18 18 2 13 15 19 5 12 18 7 13 9 8 17 10 13 15 11
8 5 14 11 18 20 17 3 11 17 13 11 4 9 17 14 19 1
10 7 8 11 13 3 19 3 12 11 12 14 4 2 12 10 14 15
12 9 13 9 16 17 12 15 6 3 11 17 13 17 14 13 4 4
19 12 3 19 3 20 19 12 8 19 8 10 19 20 3 1 7 1
16 12 4 16 2 6 15 1 13 3 7 16 5 3 16 16 14 19
12 14 6 2 11 15 9 17 15 16 19 20 14 14 20 9 4 4
6 13 16 6 3 12 12 19 11 20 4 13 9 18 7 17 8 1
4 17 3 20 3 8 12 7 4 12 6 12 1 18 13 20 20 8
4 15 1 10 2 12 8 11 5 4 20 13 12 20 1 3 3 11
1 9 2 1 16 1 12 4 5 2 7 15 12 3 9 4 13 6
13 1 10 8 5 13 20 10 6 4 8 15 8 8 20 11 9 9
2 10 11 1 18 8 20 11 18 2 3 6 14 16 19 4 3 15
output-1
57
13 6 7 3 18 14 10 16

intput-2(贪心算法会出错)
3 3 6
1 2 3
2 4 1
2 2 2
1 2 3
3 1 2
2 2 2
output-2
5
1 3 1

最小重量机器设计问题是一个经典的优化问题回溯法可以用来解决该问题。 该问题的目标是设计一个机器,使其在一定的工作条件下达到最小重量。在设计机器时,需要考虑多个因素,如结构强度、材料强度、制造成本等。因此,该问题是一个复杂的多目标优化问题回溯法是一种搜索算法,通过逐步构造可行解来寻找最优解。具体来说,回溯法会从某一个可行解开始,通过搜索和剪枝操作逐步扩展解空间,直到找到最优解或者搜索完所有可能的解。 在最小重量机器设计问题中,回溯法可以从某一个可能的机器设计开始,逐步扩展设计空间,直到找到满足所有工作条件的最小重量机器。具体来说,回溯法可以按照以下步骤进行: 1. 定义状态:将机器设计的每一个部分作为一个状态,每一个状态都有多个可选的设计方案。 2. 定义操作:定义从一个状态到另一个状态的操作,即从一个部件的一个设计方案转移到下一个部件的一个设计方案。 3. 定义约束条件:定义机器设计的工作条件,并将其作为约束条件,以确保生成的机器设计可以满足这些条件。 4. 定义目标函数:定义机器设计重量作为目标函数,以便在搜索过程中评估每一个机器设计的优劣。 5. 定义搜索策略:定义搜索顺序和剪枝规则,以便在搜索过程中尽快找到最优解。 6. 开始搜索:从一个初始状态开始,通过搜索和剪枝操作逐步扩展状态空间,直到找到最优解或者搜索完所有可能的解。 需要注意的是,回溯法虽然可以解决最小重量机器设计问题,但是在实际应用中可能存在搜索空间过大,搜索时间过长等问题。因此,在实际应用中,可能需要采用其他优化算法来解决该问题
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值