You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript格斗赛事场次排序优化:满足选手场次间隔10-30要求

格斗赛事fightNumber合规分配方案

核心思路

要实现选手相邻赛事编号差10-30的要求,不能用简单的随机或顺序分配,得用**「选手追踪+贪心筛选+回溯兜底」**的策略:先把每个选手的所有赛事归类,然后逐个分配编号时,只选符合间隔要求的赛事,遇到死路就重新来过,确保最终所有约束都满足。

具体实现代码

1. 先给选手的赛事分组

先把同一选手的所有赛事归到一起,方便后续追踪他们的参赛间隔:

// 按选手整理赛事列表
const groupFightsByFighter = (fights) => {
  const fighterMap = new Map();
  fights.forEach(fight => {
    // 假设每场赛事有redFighter和blueFighter两个选手字段,根据你的实际数据调整
    [fight.redFighter, fight.blueFighter].forEach(fighter => {
      if (!fighterMap.has(fighter)) {
        fighterMap.set(fighter, []);
      }
      fighterMap.get(fighter).push(fight);
    });
  });
  return fighterMap;
};

2. 核心分配逻辑

从第一场开始,每次只选满足间隔要求的赛事分配编号,没可选的就重新分配:

const assignFightNumbers = (fights) => {
  const fighterMap = groupFightsByFighter(fights);
  const assignedFights = new Set(); // 记录已经分配过编号的赛事
  const lastNumOfFighter = new Map(); // 记录每个选手最后一场的编号
  const result = [];

  // 随机选第一场赛事启动
  let firstFight = fights[Math.floor(Math.random() * fights.length)];
  firstFight.fightNumber = 1;
  assignedFights.add(firstFight);
  result.push(firstFight);
  [firstFight.redFighter, firstFight.blueFighter].forEach(fighter => {
    lastNumOfFighter.set(fighter, 1);
  });

  // 从2到162依次分配编号
  for (let num = 2; num <= 162; num++) {
    // 筛选符合条件的候选赛事:没分配过,且两位选手的上一场编号和当前num的差在10-30之间(或选手还没参赛)
    const validCandidates = fights.filter(fight => {
      if (assignedFights.has(fight)) return false;
      
      const redValid = !lastNumOfFighter.has(fight.redFighter) || 
                      (Math.abs(num - lastNumOfFighter.get(fight.redFighter)) >= 10 && Math.abs(num - lastNumOfFighter.get(fight.redFighter)) <= 30);
      const blueValid = !lastNumOfFighter.has(fight.blueFighter) || 
                      (Math.abs(num - lastNumOfFighter.get(fight.blueFighter)) >= 10 && Math.abs(num - lastNumOfFighter.get(fight.blueFighter)) <= 30);
      
      return redValid && blueValid;
    });

    // 没候选就重启分配(简单兜底,复杂场景可以做回溯调整)
    if (validCandidates.length === 0) {
      return assignFightNumbers(fights);
    }

    // 随机选一个候选分配编号
    const selectedFight = validCandidates[Math.floor(Math.random() * validCandidates.length)];
    selectedFight.fightNumber = num;
    assignedFights.add(selectedFight);
    result.push(selectedFight);
    
    // 更新选手的最后参赛编号
    [selectedFight.redFighter, selectedFight.blueFighter].forEach(fighter => {
      lastNumOfFighter.set(fighter, num);
    });
  }

  return result;
};

3. 验证分配结果

分配完记得检查是否符合要求,避免出现遗漏:

const checkAssignValidity = (fights) => {
  const fighterFightNums = new Map();
  fights.forEach(fight => {
    [fight.redFighter, fight.blueFighter].forEach(fighter => {
      if (!fighterFightNums.has(fighter)) {
        fighterFightNums.set(fighter, []);
      }
      fighterFightNums.get(fighter).push(fight.fightNumber);
    });
  });

  let allValid = true;
  fighterFightNums.forEach((nums, fighter) => {
    // 按编号排序后检查相邻差值
    nums.sort((a, b) => a - b);
    for (let i = 1; i < nums.length; i++) {
      const diff = Math.abs(nums[i] - nums[i-1]);
      if (diff < 10 || diff > 30) {
        console.error(`选手${fighter}的相邻赛事编号差为${diff},不符合要求`);
        allValid = false;
      }
    }
  });
  return allValid;
};

注意事项

  • 如果有选手参赛场次超过6场,162的编号范围下可能刚好卡到极限,建议先检查所有选手的参赛场次,确保理论上有可行解
  • 递归重启的方式在极端情况效率不高,可优化成带记忆的回溯算法,或者优先分配场次多的选手的赛事,减少冲突概率
  • 实际使用时,要根据你的赛事数据结构调整代码中选手字段的名称(比如你的赛事里选手字段可能不是redFighter/blueFighter)

内容的提问来源于stack exchange,提问作者Dark

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 12:48:10