CSharp:Backtracking Algorithm

项目结构:

image

 

/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : BeadItem.cs

 */
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Dto
{
    /// <summary>
    /// 多宝手串珠子实体
    /// </summary>
    public class BeadItem
    {
        public string BeadId { get; set; } = string.Empty;
        public string Name { get; set; } = string.Empty;
        public string Material { get; set; } = string.Empty;
        public string ColorGroup { get; set; } = string.Empty; // red/green/purple/gold
        public double UnitPrice { get; set; }
        public int Stock { get; set; }
    }
}



/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : JewelryItem.cs

 */
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Dto
{
    /// <summary>
    /// 成套首饰商品
    /// </summary>
    public class JewelryItem
    {
        public string SkuId { get; set; } = string.Empty;
        public string Name { get; set; } = string.Empty;
        public string Category { get; set; } = string.Empty; // necklace / earring
        public string Material { get; set; } = string.Empty;
        public string Color { get; set; } = string.Empty;
        public string Style { get; set; } = string.Empty;
        public double Price { get; set; }
        public int Stock { get; set; }
        public bool HasGem { get; set; }
    }
}



/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : IBaseRule.cs

 */

using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Rule
{
    /// <summary>
    /// 规则抽象接口
    /// </summary>
    /// <typeparam name="T">物料类型 BeadItem / JewelryItem</typeparam>
    public interface IBaseRule<T>
    {
        bool Check(T item, List<T> path);
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : BraceletRule.cs

 */

using CSharpAlgorithms.Backtracking.Dto;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Rule
{
    public class BraceletRule : IBaseRule<BeadItem>
    {
        public int MaxSingleColor { get; }

        public BraceletRule(int maxSingleColor)
        {
            MaxSingleColor = maxSingleColor;
        }

        public bool Check(BeadItem item, List<BeadItem> path)
        {
            // 库存校验
            int used = path.Count(x => x.BeadId == item.BeadId);
            if (used >= item.Stock)
                return false;

            // 色系均衡限制
            Dictionary<string, int> colorCnt = new();
            foreach (var b in path)
            {
                if (!colorCnt.ContainsKey(b.ColorGroup))
                    colorCnt[b.ColorGroup] = 0;
                colorCnt[b.ColorGroup]++;
            }

            if (colorCnt.TryGetValue(item.ColorGroup, out var count) && count >= MaxSingleColor)
                return false;

            return true;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : SceneJewelryRule.cs

 */

using CSharpAlgorithms.Backtracking.Dto;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Rule
{
    public class SceneConfig
    {
        public HashSet<string> AllowMaterial { get; set; } = new();
        public bool MustGem { get; set; }
    }

    public class SceneJewelryRule : IBaseRule<JewelryItem>
    {
        private readonly SceneConfig _config;

        public SceneJewelryRule(SceneConfig config)
        {
            _config = config;
        }

        public bool Check(JewelryItem item, List<JewelryItem> path)
        {
            if (item.Stock <= 0)
                return false;

            if (!_config.AllowMaterial.Contains(item.Material))
                return false;

            if (_config.MustGem && !item.HasGem)
                return false;

            return true;
        }

        /// <summary>
        /// 场景配置中心,新增场景只在此扩展
        /// </summary>
        /// <param name="sceneType"></param>
        /// <returns></returns>
        public static SceneConfig GetSceneConfig(string sceneType)
        {
            var map = new Dictionary<string, SceneConfig>()
        {
            {
                "wedding", new SceneConfig
                {
                    AllowMaterial = new HashSet<string>{"Au999","18K"},
                    MustGem = true
                }
            },
            {
                "commute", new SceneConfig
                {
                    AllowMaterial = new HashSet<string>{"Au999","S925","18K"},
                    MustGem = false
                }
            },
            {
                "dinner", new SceneConfig
                {
                    AllowMaterial = new HashSet<string>{"18K"},
                    MustGem = true
                }
            }
        };

            if (map.TryGetValue(sceneType, out var cfg))
                return cfg;

            return map["commute"];
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/10 21:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : ScoreUtil.cs

 */


using CSharpAlgorithms.Backtracking.Dto;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Common
{

    /// <summary>
    /// 
    /// </summary>
    public static class ScoreUtil
    {
        /// <summary>
        /// 手串方案评分:色系多样性优先
        /// </summary>
        public static double ScoreBraceletScheme(List<BeadItem> scheme)
        {
            HashSet<string> colorSet = new();
            double totalCost = 0;
            foreach (var b in scheme)
            {
                colorSet.Add(b.ColorGroup);
                totalCost += b.UnitPrice;
            }
            double diversity = colorSet.Count;
            return diversity * 10 - totalCost / 200;
        }

        /// <summary>
        /// 成套首饰方案评分
        /// </summary>
        public static double ScoreJewelryScheme(List<JewelryItem> scheme)
        {
            int gemCnt = 0;
            int stockScore = 0;
            foreach (var item in scheme)
            {
                if (item.HasGem) gemCnt++;
                stockScore += Math.Min(item.Stock, 5);
            }
            return gemCnt * 5 + stockScore;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/06 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : BraceletBackTracker.cs

 */

using CSharpAlgorithms.Backtracking.Dto;
using CSharpAlgorithms.Backtracking.Rule;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Core
{
    public class BraceletBackTracker
    {
        private readonly List<BeadItem> _beadPool;
        private readonly IBaseRule<BeadItem> _rule;
        private readonly List<List<BeadItem>> _solutions = new();

        public BraceletBackTracker(List<BeadItem> beadPool, IBaseRule<BeadItem> rule)
        {
            _beadPool = beadPool;
            _rule = rule;
        }

        private void Backtrack(List<BeadItem> path, int remain, double totalCost, double budget)
        {
            if (remain == 0)
            {
                _solutions.Add(new List<BeadItem>(path));
                return;
            }
            if (totalCost > budget)
                return;

            foreach (var bead in _beadPool)
            {
                if (!_rule.Check(bead, path))
                    continue;

                path.Add(bead);
                Backtrack(path, remain - 1, totalCost + bead.UnitPrice, budget);
                path.RemoveAt(path.Count - 1);
            }
        }

        public List<List<BeadItem>> Run(int targetCount, double budget)
        {
            _solutions.Clear();
            Backtrack(new List<BeadItem>(), targetCount, 0.0, budget);
            return _solutions;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/06 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : JewelrySceneBackTracker.cs

 */

using CSharpAlgorithms.Backtracking.Dto;
using CSharpAlgorithms.Backtracking.Rule;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Core
{

    /// <summary>
    /// 
    /// </summary>
    public class JewelrySceneBackTracker
    {
        private readonly List<JewelryItem> _goodsPool;
        private readonly IBaseRule<JewelryItem> _rule;
        private readonly List<List<JewelryItem>> _solutions = new();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="goodsPool"></param>
        /// <param name="rule"></param>
        public JewelrySceneBackTracker(List<JewelryItem> goodsPool, IBaseRule<JewelryItem> rule)
        {
            _goodsPool = goodsPool;
            _rule = rule;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="startIdx"></param>
        /// <param name="selected"></param>
        /// <param name="totalPrice"></param>
        /// <param name="budget"></param>
        /// <param name="targetCategories"></param>
        private void Backtrack(int startIdx, List<JewelryItem> selected, double totalPrice, double budget, HashSet<string> targetCategories)
        {
            HashSet<string> selectedCats = selected.Select(x => x.Category).ToHashSet();

            // 是否集齐所有目标品类
            bool complete = targetCategories.All(selectedCats.Contains);
            if (complete)
            {
                _solutions.Add(new List<JewelryItem>(selected));
                return;
            }

            if (totalPrice > budget)
                return;

            for (int i = startIdx; i < _goodsPool.Count; i++)
            {
                var item = _goodsPool[i];
                if (selectedCats.Contains(item.Category))
                    continue;

                if (!_rule.Check(item, selected))
                    continue;

                selected.Add(item);
                Backtrack(i + 1, selected, totalPrice + item.Price, budget, targetCategories);
                selected.RemoveAt(selected.Count - 1);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="budget"></param>
        /// <param name="targetCategories"></param>
        /// <returns></returns>
        public List<List<JewelryItem>> Run(double budget, HashSet<string> targetCategories)
        {
            _solutions.Clear();
            Backtrack(0, new List<JewelryItem>(), 0.0, budget, targetCategories);
            return _solutions;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/06 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : BraceletMatchService.cs

 */

using CSharpAlgorithms.Backtracking.Common;
using CSharpAlgorithms.Backtracking.Core;
using CSharpAlgorithms.Backtracking.Dto;
using CSharpAlgorithms.Backtracking.Rule;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Service
{

    /// <summary>
    /// 
    /// </summary>
    public class BraceletMatchService
    {
        private readonly List<BeadItem> _beadPool;

        public BraceletMatchService(List<BeadItem> beadPool)
        {
            _beadPool = beadPool;
        }

        public List<List<BeadItem>> Match(int targetCount, double budget, int maxColorLimit, int topN)
        {
            IBaseRule<BeadItem> rule = new BraceletRule(maxColorLimit);
            var tracker = new BraceletBackTracker(_beadPool, rule);
            var schemes = tracker.Run(targetCount, budget);

            // 打分降序
            schemes.Sort((a, b) => ScoreUtil.ScoreBraceletScheme(b).CompareTo(ScoreUtil.ScoreBraceletScheme(a)));

            if (schemes.Count > topN)
                schemes = schemes.Take(topN).ToList();

            return schemes;
        }
    }
}


/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/06 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : JewelrySceneMatchService.cs

 */

using CSharpAlgorithms.Backtracking.Common;
using CSharpAlgorithms.Backtracking.Core;
using CSharpAlgorithms.Backtracking.Dto;
using CSharpAlgorithms.Backtracking.Rule;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Backtracking.Service
{


    /// <summary>
    /// 
    /// </summary>
    public class JewelrySceneMatchService
    {
        private readonly List<JewelryItem> _goodsPool;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="goodsPool"></param>
        public JewelrySceneMatchService(List<JewelryItem> goodsPool)
        {
            _goodsPool = goodsPool;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="budget"></param>
        /// <param name="targetCategories"></param>
        /// <param name="topN"></param>
        /// <returns></returns>
        public List<List<JewelryItem>> MatchByScene(string scene, double budget, HashSet<string> targetCategories, int topN)
        {
            var config = SceneJewelryRule.GetSceneConfig(scene);
            IBaseRule<JewelryItem> rule = new SceneJewelryRule(config);
            var tracker = new JewelrySceneBackTracker(_goodsPool, rule);
            var schemes = tracker.Run(budget, targetCategories);

            schemes.Sort((a, b) => ScoreUtil.ScoreJewelryScheme(b).CompareTo(ScoreUtil.ScoreJewelryScheme(a)));

            if (schemes.Count > topN)
                schemes = schemes.Take(topN).ToList();

            return schemes;
        }
    }
}

  

/*
 # encoding: utf-8 
# 版权所有  2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述: Backtracking Algorithm
# Author    : geovindu,Geovin Du 涂聚文.
# IDE       : vs2026 c# .net 10
# os        : windows 10
# database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
# Datetime  : 2026/07/06 22:16 
# User      :  geovindu
# Product   : Visual Studio 2026
# Project   : DesignAlgorithm
# File      : BacktrackingBll.cs

 */

using CSharpAlgorithms.Backtracking.Dto;
using CSharpAlgorithms.Backtracking.Service;
using System;
using System.Collections.Generic;
using System.Text;

namespace CSharpAlgorithms.Bll
{


    /// <summary>
    /// 
    /// </summary>
    public class BacktrackingBll
    {



        static void TestBraceletMatch()
        {
            var beadPool = new List<BeadItem>()
        {
            new BeadItem{BeadId = "B01", Name = "南红圆珠", Material = "南红", ColorGroup = "red", UnitPrice = 168, Stock = 4},
            new BeadItem{BeadId = "B02", Name = "和田玉圆珠", Material = "和田玉", ColorGroup = "green", UnitPrice = 198, Stock = 5},
            new BeadItem{BeadId = "B03", Name = "紫水晶", Material = "紫水晶", ColorGroup = "purple", UnitPrice = 128, Stock = 4},
            new BeadItem{BeadId = "B04", Name = "足金隔珠", Material = "足金", ColorGroup = "gold", UnitPrice = 320, Stock = 3},
        };

            var svc = new BraceletMatchService(beadPool);
            var result = svc.Match(targetCount: 8, budget: 2000, maxColorLimit: 4, topN: 6);

            Console.WriteLine("===== 多宝手串搭配方案 =====");
            for (int idx = 0; idx < result.Count; idx++)
            {
                var scheme = result[idx];
                double total = scheme.Sum(x => x.UnitPrice);
                var names = scheme.Select(x => x.Name).ToList();
                Console.WriteLine($"方案{idx + 1} 总价:{total:F2} 珠子:[{string.Join(", ", names)}]");
            }
        }

        static void TestJewelrySceneMatch()
        {
            var goodsPool = new List<JewelryItem>()
        {
            new JewelryItem{SkuId="N001",Name="碎钻项链",Category="necklace",Material="18K",Color="white",Style="luxury",Price=3299,Stock=12,HasGem=true},
            new JewelryItem{SkuId="N003",Name="素金项链",Category="necklace",Material="Au999",Color="yellow",Style="minimalist",Price=2199,Stock=9,HasGem=false},
            new JewelryItem{SkuId="E001",Name="白钻耳饰",Category="earring",Material="18K",Color="white",Style="luxury",Price=2199,Stock=15,HasGem=true},
            new JewelryItem{SkuId="E003",Name="素金耳饰",Category="earring",Material="Au999",Color="yellow",Style="minimalist",Price=1399,Stock=11,HasGem=false},
        };

            var svc = new JewelrySceneMatchService(goodsPool);
            var targetCats = new HashSet<string> { "necklace", "earring" };

            Console.WriteLine("\n===== 婚嫁场景 =====");
            var wedding = svc.MatchByScene("wedding", 8000, targetCats, 8);
            foreach (var set in wedding)
            {
                var names = set.Select(x => x.Name).ToList();
                double total = set.Sum(x => x.Price);
                Console.WriteLine($"[{string.Join(", ", names)}] 总价 {total:F2}");
            }

            Console.WriteLine("\n===== 通勤场景 =====");
            var commute = svc.MatchByScene("commute", 5000, targetCats, 8);
            foreach (var set in commute)
            {
                var names = set.Select(x => x.Name).ToList();
                double total = set.Sum(x => x.Price);
                Console.WriteLine($"[{string.Join(", ", names)}] 总价 {total:F2}");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Demo()
        {
            TestBraceletMatch();
            TestJewelrySceneMatch();
        }




    }
}

  

输出:

c08d9728c32f42f548315085cdeb5490

 

posted @ 2026-07-23 22:16  ®Geovin Du Dream Park™  阅读(4)  评论(0)    收藏  举报