回溯法求解0/1背包问题

问题描述:装入背包中物品重量和恰好为W
解法一

#include<stdio.h>
#define MAXN 20                 // 最多物品数
// 问题表示
int n = 4;                      // 4种物品
int W = 6;                      // 限制重量为6
int w[] = {0, 5, 3, 2, 1};      // 存放4个物品重量,不用下标0元素
int v[] = {0, 4, 4, 3, 1};      // 存放4个物品价值,不用下标0元素
// 求解结果表示
int x[MAXN];                    // 存放最终解
int maxv = 0;                   // 存放最优解的总价值
void dfs(int i, int tw, int tv, int op[]){      // 求解0/1背包问题
    if(i > n){                                  // 找到一个叶子节点
        if(tw == W && tv > maxv){               // 找到一个满足条件的更优解,保持它
            maxv = tv;
            for(int j = 1; j <= n; j++)
                x[j] = op[j];
        }
    }
    else {
        op[i] = 1;              // 尚未找完所有物品
        dfs(i+1, tw+w[i],tv+v[i],op);
        op[i] = 0;
        dfs(i+1, tw, tv, op);
    }
}
void dispasolution(){           // 输出最优解
    printf("最佳装填方案:\n");
    for(int i = 0; i <= n; i++){
        if(x[i] == 1)
            printf("    选取第%d个物品\n",i);
    }
    printf("总重量=%d,总价值=%d\n",W,maxv);
}
int main(){
    int op[MAXN];               // 存放临时解
    dfs(1,0,0,op);              // i从1开始
    dispasolution();
    return 0;
}

解法二:左剪枝

#include<stdio.h>
#define MAXN 20                 // 最多物品数
// 问题表示
int n = 4;                      // 4种物品
int W = 6;                      // 限制重量为6
int w[] = {0, 5, 3, 2, 1};      // 存放4个物品重量,不用下标0元素
int v[] = {0, 4, 4, 3, 1};      // 存放4个物品价值,不用下标0元素
// 求解结果表示
int x[MAXN];                    // 存放最终解
int maxv = 0;                   // 存放最优解的总价值
void dfs(int i, int tw, int tv, int op[]){      // 求解0/1背包问题
    if(i > n){                                  // 找到一个叶子节点
        if(tw == W && tv > maxv){               // 找到一个满足条件的更优解,保持它
            maxv = tv;
            for(int j = 1; j <= n; j++)
                x[j] = op[j];
        }
    }
    else {                      // 尚未找完所有物品
        // 解法一
        // op[i] = 1;
        // dfs(i+1, tw+w[i],tv+v[i],op);
        // 解法二,左剪枝
        if(tw+w[i] <= W){       // 左孩子结点剪枝
            op[i] = 1;          // 选取第i个物品
            dfs(i+1, tw+w[i],tv+v[i],op);
        }
        op[i] = 0;              // 不选取第i个物品,回溯
        dfs(i+1, tw, tv, op);
    }
}
void dispasolution(){           // 输出最优解

    printf("最佳装填方案:\n");
    for(int i = 0; i <= n; i++){
        if(x[i] == 1)
            printf("    选取第%d个物品\n",i);
    }
    printf("总重量=%d,总价值=%d\n",W,maxv);
}
int main(){
    int op[MAXN];               // 存放临时解
    dfs(1,0,0,op);              // i从1开始
    dispasolution();
    return 0;
}

解法三:右剪枝

#include<stdio.h>
#define MAXN 20                 // 最多物品数
// 问题表示
int n = 4;                      // 4种物品
int W = 6;                      // 限制重量为6
int w[] = {0, 5, 3, 2, 1};      // 存放4个物品重量,不用下标0元素
int v[] = {0, 4, 4, 3, 1};      // 存放4个物品价值,不用下标0元素
// 求解结果表示
int x[MAXN];                    // 存放最终解
int maxv = 0;                   // 存放最优解的总价值
void dfs(int i, int tw, int tv, int rw, int op[]){      // 求解0/1背包问题
    if(i > n){                                  // 找到一个叶子节点
        if(tw == W && tv > maxv){               // 找到一个满足条件的更优解,保持它
            maxv = tv;
            for(int j = 1; j <= n; j++)
                x[j] = op[j];
        }
    }
    else {                      // 尚未找完所有物品
        // 解法一
        // op[i] = 1;
        // dfs(i+1, tw+w[i],tv+v[i],op);
        // 解法二:左剪枝
        if(tw+w[i] <= W){       // 左孩子结点剪枝
            op[i] = 1;          // 选取第i个物品
            dfs(i+1, tw+w[i], tv+v[i], rw-w[i], op);
        }
        // op[i] = 0;              // 不选取第i个物品,回溯
        // dfs(i+1, tw, tv, op);
        // 解法三:右剪枝
        if(tw + rw - w[i] >= W){    // 右孩子结点剪枝
            op[i] = 0;              // 不选取第i个物品,回溯
            dfs(i+1, tw, tv, rw-w[i], op);
        }
    }
}
void dispasolution(){           // 输出最优解

    printf("最佳装填方案:\n");
    for(int i = 0; i <= n; i++){
        if(x[i] == 1)
            printf("    选取第%d个物品\n",i);
    }
    printf("总重量=%d,总价值=%d\n",W,maxv);
}
int main(){
    int op[MAXN];               // 存放临时解
    int rw=0;
	for(int i=1;i<=n;i++)
		rw+=w[i];
    dfs(1,0,0,rw,op);              // i从1开始
    dispasolution();
    return 0;
}

运行结果
运行结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值