首页 新闻 会员 周边

基于分治法计算背包问题。在获取最大价值的同时,如果获得放入背包里的所有物品列表?

0
悬赏园豆:200 [已解决问题] 解决于 2023-12-09 23:53
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KnapsackProblem
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int KnapsackWeight = 10;
            List<AvailableInfo> Availables = new List<AvailableInfo>();
            Availables.Add(new AvailableInfo() { Weight = 2, Value = 6 });
            Availables.Add(new AvailableInfo() { Weight = 2, Value = 3 });
            Availables.Add(new AvailableInfo() { Weight = 6, Value = 5 });
            Availables.Add(new AvailableInfo() { Weight = 5, Value = 4 });
            Availables.Add(new AvailableInfo() { Weight = 4, Value = 6 });
            int result = DivideandConquer(Availables, 0, KnapsackWeight);
            richTextBox1.Text = result.ToString() + Environment.NewLine;
            
        }

        /// <summary>
        /// 分治法
        /// </summary>
        /// <param name="availables">物品列表</param>
        /// <param name="index">当前分配索引</param>
        /// <param name="availableWeight">当前可用重量</param>
        /// <returns></returns>
        private int DivideandConquer(List<AvailableInfo> availables, int index, int availableWeight)
        {
            int result = 0;
            if (index >= availables.Count || availableWeight <= 0)
            {
                return 0;//如果索引超出物品列表或者可用重量小于0
            }
            else if (availables[index].Weight > availableWeight)
            {
                //如果装不下,且直接计算下一个物品
                result = DivideandConquer(availables, index + 1, availableWeight);
            }
            else
            {
                // 可以放下,比较放入当前物品和不放入当前物品的结果,取价值大的一方
                result = Math.Max(DivideandConquer(availables, index + 1, availableWeight - availables[index].Weight) + availables[index].Value, DivideandConquer(availables, index + 1, availableWeight));
            }
            return result;
        }

    }

    public class AvailableInfo
    {
        public AvailableInfo() { }

        /// <summary>
        /// 物品重量
        /// </summary>
        public int Weight { get; set; }
        /// <summary>
        /// 物品价值
        /// </summary>
        public int Value { get; set; }
    }
}
小豆子ddg的主页 小豆子ddg | 初学一级 | 园豆:22
提问于:2023-12-01 12:19
< >
分享
最佳答案
0

如果你希望在解决背包问题的同时获得放入背包的所有物品列表,你可以对分治法进行一些修改,以记录所选择的物品。在这里,我对你提供的代码进行了修改,添加了一个用于存储选择的物品的列表 selectedItems,以及一个用于记录当前最优解的列表 bestItems。以下是修改后的代码:

csharp

// 在 Form1 类中添加两个列表
List<AvailableInfo> selectedItems = new List<AvailableInfo>();
List<AvailableInfo> bestItems = new List<AvailableInfo>();

// button1_Click 方法
private void button1_Click(object sender, EventArgs e)
{
int KnapsackWeight = 10;
List<AvailableInfo> Availables = new List<AvailableInfo>();
Availables.Add(new AvailableInfo() { Weight = 2, Value = 6 });
Availables.Add(new AvailableInfo() { Weight = 2, Value = 3 });
Availables.Add(new AvailableInfo() { Weight = 6, Value = 5 });
Availables.Add(new AvailableInfo() { Weight = 5, Value = 4 });
Availables.Add(new AvailableInfo() { Weight = 4, Value = 6 });

// 重置列表
selectedItems.Clear();
bestItems.Clear();

int result = DivideandConquer(Availables, 0, KnapsackWeight);

// 将选择的物品显示在 RichTextBox 中
richTextBox1.Text = "最大价值: " + result.ToString() + Environment.NewLine;
richTextBox1.Text += "选择的物品列表:" + Environment.NewLine;
foreach (var item in bestItems)
{
    richTextBox1.Text += $"Weight: {item.Weight}, Value: {item.Value}" + Environment.NewLine;
}

}

// 修改 DivideandConquer 方法,记录选择的物品
private int DivideandConquer(List<AvailableInfo> availables, int index, int availableWeight)
{
int result = 0;
if (index >= availables.Count || availableWeight <= 0)
{
return 0;
}
else if (availables[index].Weight > availableWeight)
{
result = DivideandConquer(availables, index + 1, availableWeight);
}
else
{
// 在两种情况中选择总价值更大的一方,并记录选择的物品列表
int withCurrentItem = DivideandConquer(availables, index + 1, availableWeight - availables[index].Weight) + availables[index].Value;
int withoutCurrentItem = DivideandConquer(availables, index + 1, availableWeight);

    if (withCurrentItem > withoutCurrentItem)
    {
        // 选择当前物品
        selectedItems.Add(availables[index]);
        result = withCurrentItem;
    }
    else
    {
        // 不选择当前物品
        result = withoutCurrentItem;
    }
}

// 更新最优解的物品列表
bestItems.Clear();
bestItems.AddRange(selectedItems);

return result;

}
上述修改中,我添加了 selectedItems 用于存储当前选择的物品列表,以及 bestItems 用于存储当前最优解的物品列表。在递归过程中,根据选择情况更新这两个列表。在最终显示结果时,将 bestItems 中的物品列表显示在 RichTextBox 中。

收获园豆:200
Technologyforgood | 大侠五级 |园豆:5718 | 2023-12-01 21:57

感谢回复。

这几天我也一直在用类似的方法在寻求答案,最终用类似的方式找到了解决的途径。

你给出的办法,一旦物品清单比较大时,bestItems里的数据会有大量的重复数据,给回溯增加了难度。

以下是我的解决办法:

和你的思路一样,每次出现"选择当前物品"时,也记录被选择的物品,但同时还在记录index参数。

得到最优解后,将最终的Value放入bestItems,按Index顺序回溯。直到Value被回归到0值。

依然非常感觉。

小豆子ddg | 园豆:22 (初学一级) | 2023-12-09 23:53
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册