基于泛型链表的回溯算法实现:8小时内优先选高价值任务
回溯算法实现高优先级任务组合选择方案
核心逻辑梳理
你的泛型链表已经按「Urgent>Return>Normal」优先级降序、同优先级按price降序排好序了,这正好贴合回溯需求——先优先处理高优先级任务,再依次考虑低优先级,回溯过程中只需要按链表顺序遍历,自然就能保证高优先级任务的选择优先级,同时通过剪枝减少无效递归。
完善ITask接口(确保必要属性)
先确认ITask必须包含以下方法/属性(如果还没定义):
Priority GetPriority():返回任务优先级枚举(Urgent/Return/Normal)int GetDuration():获取任务耗时(单位:小时)int GetPrice():获取任务收益string GetTaskId():任务唯一标识(用于记录组合)
调整BacktrackBase抽象类
给抽象类补充回溯需要的状态变量和核心方法框架:
public abstract class BacktrackBase<T> where T : ITask { // 最优解存储 protected int _maxTotalPrice = 0; protected List<T> _bestCombination = new List<T>(); protected const int MAX_ALLOWED_DURATION = 8; // 8小时时长限制 // 当前回溯状态 protected int _currentTotalDuration = 0; protected int _currentTotalPrice = 0; protected List<T> _currentSelected = new List<T>(); // 回溯入口方法 public abstract void StartBacktrack(GenerikusLancoltLista<T> sortedTasks); // 递归回溯核心方法 protected abstract void Backtrack(GenerikusLancoltLista<T> sortedTasks, int currentIndex); }
实现具体回溯类TaskBacktracker
基于抽象类实现具体逻辑,重点利用链表的排序顺序,加上剪枝优化:
public class TaskBacktracker : BacktrackBase<ITask> { // 上界剪枝用的剩余收益前缀和数组 private int[] _remainingMaxPrice; public override void StartBacktrack(GenerikusLancoltLista<ITask> sortedTasks) { // 初始化状态 _maxTotalPrice = 0; _bestCombination.Clear(); _currentTotalDuration = 0; _currentTotalPrice = 0; _currentSelected.Clear(); // 预处理剩余收益前缀和,用于上界剪枝 _remainingMaxPrice = new int[sortedTasks.Count + 1]; _remainingMaxPrice[sortedTasks.Count] = 0; for (int i = sortedTasks.Count - 1; i >= 0; i--) { _remainingMaxPrice[i] = _remainingMaxPrice[i + 1] + sortedTasks[i].GetPrice(); } // 从第一个任务开始递归回溯 Backtrack(sortedTasks, 0); } protected override void Backtrack(GenerikusLancoltLista<ITask> sortedTasks, int currentIndex) { // 更新最优解:如果当前组合收益更高,替换最优解 if (_currentTotalPrice > _maxTotalPrice) { _maxTotalPrice = _currentTotalPrice; // 深拷贝当前组合,避免后续修改影响最优解 _bestCombination = new List<ITask>(_currentSelected); } // 上界剪枝:当前收益+剩余所有任务最大收益都不如已有的最优解,直接终止递归 if (_currentTotalPrice + _remainingMaxPrice[currentIndex] <= _maxTotalPrice) { return; } // 遍历从currentIndex开始的任务(避免重复组合,保证按顺序选择) for (int i = currentIndex; i < sortedTasks.Count; i++) { ITask task = sortedTasks[i]; int taskDuration = task.GetDuration(); // 超时剪枝:加入当前任务后超过8小时,直接跳过(同优先级后续任务收益更低,没必要继续) if (_currentTotalDuration + taskDuration > MAX_ALLOWED_DURATION) { continue; } // 选择当前任务,更新状态 _currentSelected.Add(task); _currentTotalDuration += taskDuration; _currentTotalPrice += task.GetPrice(); // 递归处理下一个任务 Backtrack(sortedTasks, i + 1); // 回溯:撤销选择,恢复状态 _currentSelected.RemoveAt(_currentSelected.Count - 1); _currentTotalDuration -= taskDuration; _currentTotalPrice -= task.GetPrice(); } } // 对外提供获取最优组合和最大收益的方法 public List<ITask> GetBestTaskCombination() => _bestCombination; public int GetMaxTotalPrice() => _maxTotalPrice; }
关键优化说明
- 顺序遍历保证优先级:因为链表已经按优先级降序排列,回溯时从左到右遍历,自然会先尝试选择高优先级任务,符合需求。
- 超时剪枝:如果当前任务加入后超时,直接跳过,同优先级后续任务收益更低,不需要再考虑。
- 上界剪枝:通过预处理剩余任务的最大可能收益总和,提前终止不可能超过当前最优解的递归分支,大幅提升效率。
使用示例
// 假设你已经有排序好的GenerikusLancoltLista<ITask> sortedTaskList var tracker = new TaskBacktracker(); tracker.StartBacktrack(sortedTaskList); var bestTasks = tracker.GetBestTaskCombination(); var maxPrice = tracker.GetMaxTotalPrice(); Console.WriteLine($"最优组合总收益:{maxPrice},总耗时:{bestTasks.Sum(t => t.GetDuration())}小时"); foreach (var task in bestTasks) { Console.WriteLine($"任务ID:{task.GetTaskId()} | 优先级:{task.GetPriority()} | 耗时:{task.GetDuration()}h | 收益:{task.GetPrice()}"); }
内容的提问来源于stack exchange,提问作者Levente Tóth
相关产品推荐
相关产品推荐

