算法设计与分析第五章作业

目录

1. 请用回溯法的方法分析“最小重量机器设计问题”

 最小重量机器设计问题描述

问题描述:

输入格式:

输出格式:

输入样例:

输出样例:

回溯法的基本步骤:

常用剪枝函数:

1.1 说明“最小重量机器设计问题”的解空间

1.2 说明 “最小重量机器设计问题"的解空间树

1.3 在遍历解空间树的过程中,每个结点的状态值是什么

1.4 代码

2. 你对回溯算法的理解


1. 请用回溯法的方法分析“最小重量机器设计问题”

 最小重量机器设计问题描述

问题描述:

设某一机器由n个部件组成,每一种部件都可以从m个不同的供应商处购得。设wij​是从供应商j 处购得的部件i的重量,cij​是相应的价格。
试设计一个算法,给出总价格不超过d的最小重量机器设计。

输入格式:

第一行有3 个正整数n ,m和d, 0<n<30, 0<m<30, 接下来的2n 行,每行m个数。前n行是c,后n行是w。

输出格式:

输出计算出的最小重量,以及每个部件的供应商。

输入样例:

3 3 4
1 2 3
3 2 1
2 2 2
1 2 3
3 2 1
2 2 2

输出样例:

4
1 3 1 

回溯法的基本步骤:

        (1)针对所给问题,定义问题的解空间;

        (2)确定易于搜索的解空间结构;

        (3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索

常用剪枝函数:

        用约束函数在扩展结点处剪去不满足约束的子树;

        用限界函数剪去得不到最优解的子树。

1.1 说明“最小重量机器设计问题”的解空间

        理解问题:一共购置n个部件,每个部件可以从m个供应商手中获得,故每个部件有m中选择。

所有可能结果:

{1, 1, ……,1}、{2, 1, ……,1}、{3, 1, ……, 1}

  ……

{m,m,……,m}  

每个大括号里一共n个数字,对应n个部件,取值从1到m对应m个供应商,每个部件都分别有m种可能的选择,一共有m^n种可能的结果。

1.2 说明 “最小重量机器设计问题"的解空间树

        不剪枝看,这棵解空间树是一棵m叉树,一共n层,第几层结点的状态值就对应第几个部件的选择,是哪个供应商;添加剪枝函数,使得该树被剪枝,删去不满足价格小于d以及得不到最优解的可能,最终使得整体在满足价格不超过d的条件下购置n个商品的最小重量。

剪枝函数:

        约束函数contains():满足当前已购置商品的价格不超过d

        限界函数bound():判断当前已购置商品的重量不会超过已获得的最优解(价格不超过d条件下的最小重量),如果超过了,则不用再往下遍历该结点了,肯定得不到最优解,回溯看上一步选择其他值是否可能得到最优解。

1.3 在遍历解空间树的过程中,每个结点的状态值是什么

        假设该结点是第i层的结点,同时该结点的值为j,表示状态为第i个部件选择在第j个供应商处购置。

1.4 代码

#include <iostream>
using namespace std; 

int n; // n个部件
int m; // m个供应商
int w[30][30];  // w[i][j]:从供应商j处购得部件i的重量
int c[30][30];  // c[i][j]:从供应商j处购得部件i的价格
// 1 <= i <= n ; 1 <= j <= m
int x[30], bestx[30];  // 1到n个部件分别选择的供应商
int leastw = 0x3f3f3f3f;  // 预算d条件下购置n个部件的最小重量
int cw;  // 当前重量
int cost;  // 当前价格
int d;  // 总价格不超过d

bool contains(int t) { // 限界
    return (cost + c[t][x[t]] <= d);
}

bool bound(int t) {
    return (cw + w[t][x[t]] < leastw);
}

void backtrack(int t) { // 依次判断1到n个部件
    if(t > n) {
        if(cw < leastw) {
            leastw = cw;
            for(int i = 1; i <= n; i++)
                bestx[i] = x[i];
        }
        return;
    }
    for(int i = 1; i <= m; i++) { // 依次观察m个供应商
        x[t] = i;
        if(contains(t) && bound(t)) {// 剪枝:价格小于d条件下,重量尽可能小
            cost += c[t][x[t]];
            cw += w[t][x[t]];
            backtrack(t+1);
            cost -= c[t][x[t]];
            cw -= w[t][x[t]];
        }
    }
}

int main() {
    cin >> n >> m >> d;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cin >> c[i][j];
        }
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cin >> w[i][j];
        }
    }
    
    backtrack(1);
    cout << leastw << endl;
    for(int i = 1; i <= n; i++) {
        cout << bestx[i] << " ";
    }
}

2. 你对回溯算法的理解

        目前我所学过的解空间树分为子集树、排列树和n叉树(最小重量机器设计问题),思想都大致相同,像是“试错”,把每种可能的结果走一遍,不行(用剪枝函数)或到叶子结点就回溯,继续看另一种结果是否可以获得想要的结果。

        我目前写代码的步骤,首先是把所有可能的结果都想到,构建解空间;之后是将所有可能的结果在一棵解空间树上体现出来;最后是用深度搜索的方法遍历解空间树,同时根据需要采用剪枝函数,删去不可行结果和减少遍历时间,当遍历完,答案也就出来了。

       

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值