算法设计与分析-回溯法

问题A: 回溯法-子集问题

题目描述

输入一个整数n表示有n(1<=n<=20)种不同的物品,要求输出这些物品的所有可能子集(如果子集中有第i个物品,则在对应的位置上输出1,否则在对应的位置上输出0;另外子集对应的二进制数要求从小到大输出,以保证答案唯一)

输入

第1行输入一个整数

输出

后面各行依次从小到大输出子集对应的二进制数

样例输入

 复制

3
样例输出

 复制

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1
#include <stdio.h>

int n;//存储物品个数
int x[21]; //当前解,0 号单元不存储有效数据

void output() {
    int i;
    for (i = 1; i <= n; i++) {
        printf("%d ", x[i]);
    }
    printf("\n");
}

void backtrack(int i) { // 搜索第 i 层结点

    if (i > n) { //到达叶结点考虑结束本次递归
        output();
    } else {
        x[i] = 0;
        backtrack(i + 1);
        x[i] = 1;
        backtrack(i + 1);
    }

}

int main(void) {
    scanf("%d", &n);
    backtrack(1);
    return 0;
}

问题B: 回溯法-01背包问题

题目描述

给定n(1<=n<=20)种物品和一背包,物品i (1≤i≤n) 的重量是wi (wi > 0),其价值为vi (vi > 0),背包的容量为c, 问应如何选择装入背包的物品,使得装入背包中物品的总价值最大? 为了方便编写限界函数,假设按照单位重量获利递减输入每个物品的重量和获利。

输入

第一行输入两个整数,分别表示n个物品和背包容量c
第二行输入n个整数,分别表示每个物品的重量
第三输入n个整数,分别表示每个物品的价值

输出

第一行输出最大价值
第二行输出最解向量的值(1表示放入背包,0表示没有放入背包)

样例输入

 复制

3 10
3 4 5
4 5 6
样例输出

 复制

11
0 1 1
#include <stdio.h>  
  
int w[21]; // 存储物品重量  
int v[21]; // 存储物品价值  
int n;     // 存储物品个数  
int c;     // 存储背包容量  
int bestx[21]; // 最优解  
int x[21]; // 当前解  
int bestv; // 最优值  
int cv;    // 当前已经放到背包中的物品价值  
int cw;    // 当前已经放到背包中的物品重量  
  
double bound(int i) { // 计算剩余物品(i:n)的上界  
    int cleft = c - cw;  // 剩余容量  
    double b = 0.0; // 使用double来避免整数除法导致的精度损失  
    while (i <= n && w[i] <= cleft) {  
        cleft -= w[i];  
        b += v[i];  
        i++;  
    }  
    if (i <= n) {  
        b += (double)v[i] / w[i] * cleft; // 装满背包  
    }  
    return b;  
}  
  
void backtrack(int i) {  
    if (i > n) { // 到达叶结点,更新最优解(如果当前价值更高)  
        if (cv > bestv) {  
            bestv = cv;  
            for (int j = 1; j <= n; j++) {  
                bestx[j] = x[j];  
            }  
        }  
        return;  
    }  
  
    // 不选择第i个物品,搜索右子树  
    if (cv + bound(i + 1) > bestv) { // 如果限界函数认为可能有更优解  
        backtrack(i + 1); // 不放入第i个物品,继续搜索  
    }  
  
    // 选择第i个物品,搜索左子树  
    if (cw + w[i] <= c) { // 满足背包容量限制  
        x[i] = 1; // 标记为已选择  
        cw += w[i]; // 更新当前重量  
        cv += v[i]; // 更新当前价值  
        backtrack(i + 1); // 继续搜索  
        // 回溯  
        x[i] = 0; // 撤销选择  
        cw -= w[i]; // 撤销重量  
        cv -= v[i]; // 撤销价值  
    }  
}  
  
int main(void) {  
    scanf("%d %d", &n, &c);  
  
    for (int i = 1; i <= n; i++) {  
        scanf("%d", &w[i]);  
    }  
  
    for (int i = 1; i <= n; i++) {  
        scanf("%d", &v[i]);  
    }  
  
    cw = 0;  
    cv = 0;  
    bestv = 0; // 初始化最优价值为0  
  
    backtrack(1);  
  
    printf("%d\n", bestv);  
  
    for (int i = 1; i <= n; i++) {  
        printf("%d ", bestx[i]);  
    }  
    printf("\n");  
  
    return 0;  
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值