算法分组题

275 阅读3分钟

一、题目:公司有n个组,>=5,每组人数相同,>=2人,需要进行随机的组队吃饭。

二、要求

  1. 两两一队或三人一队,不能落单
  2. 两人队、三人队各自的队伍数均不得少于2
  3. 一个人只出现一次
  4. 队伍中所有人不能来自相同组
  5. 随机组队,重复执行程序得到的结果不一样,总队伍数也不能一样
  6. 注释注释注释
    注:要同时满足条件1-6,

举例

小组列表

var GroupList = [
            ['小名', '小红', '小马', '小丽', '小强'],
            ['大壮', '大力', '大1', '大2', '大3'],
            ['阿花', '阿朵', '阿蓝', '阿紫', '阿红'],
            ['A', 'B', 'C', 'D', 'E'],
            ['一', '二', '三', '四', '五'],
            ['建国', '建党', '建民', '建超', '建跃'],
            ['爱民', '爱军', '爱国', '爱辉', '爱月']
        ]

输入:GroupList 示例输出: 小强 大3] [阿红 E] [五 建跃] [爱月 小名] [大壮 阿花] [A 一] [建国 爱民] [小红 大力] [阿朵 B] [二 建党] [爱军 小马] [大1 阿蓝] [C 三] [建民 爱国 小丽] [大2 阿紫 D] [四 建超 爱辉]

三、解决思路

总共6个条件,其中一个是注释

  1. 第一个条件是只能是两个或者三个一组,可以先随机生成2个一组的队伍,和三个一组的队伍,但是可能会出现最后一个队伍是一个人,此时需要把这个人加入到2个人一组的队伍中(ps:因为同组人不可以一个队伍,所以需要判断是否有在一组的人在这个队伍)
  2. 两人队、三人队各自的队伍数均不得少于2,所以生成结束后可以判断,或者提前生成队伍,看看队伍是否符合不少于2。这里还需要判断是否落单,因为落单的时候需要加入2人的队伍,所以两人队伍会变成3人队伍。
  3. 一个人只出现一次,需要标记是否已经使用
  4. 队伍中所有人不能来自相同组,这里需要判是否为同一组
  5. 随机组队,重复执行程序得到的结果不一样,总队伍数也不能一样,这里组队就必须用随机,不能hard code

四、代码实现

todo 那个地方,我觉得基本不可能出现,2,3 人队伍数量小于二,但是随机就有可能,所以可以提前把队伍数随机出来,如果出现,就重新随机一次


func main() {
   r := rand.New(rand.NewSource(time.Now().UnixNano()))

   groupList := [][]string{
      {"小名", "小红", "小马", "小丽", "小强"},
      {"大壮", "大力", "大1", "大2", "大3"},
      {"阿花", "阿朵", "阿蓝", "阿紫", "阿红"},
      {"A", "B", "C", "D", "E"},
      {"一", "二", "三", "四", "五"},
      {"建国", "建党", "建民", "建超", "建跃"},
      {"爱民", "爱军", "爱国", "爱辉", "爱月"},
   }

   // 计算总人数和每组人数
   numGroups := len(groupList)
   numPeoplePerGroup := len(groupList[0])
   totalPeople := numGroups * numPeoplePerGroup

   // 初始化结果数组
   result := make([][]string, totalPeople/2)
   // 初始化标记数组
   var usedGroups = make([][]bool, 0, 10)
   for i := 0; i < numGroups; i++ {
      usedGroups = append(usedGroups, make([]bool, numPeoplePerGroup))
   }
   counter := 0
   var groupIndex = 0 //记录最后一个最的索引
   // 随机分组
   for i := 0; i < len(result); i++ {
      var team []string

      // todo 随机选取2人或3人组队 这里可以把所有的随机先随机出来,然后判断是否2、3 都大于两个
      numPeople := r.Intn(2) + 2

      for len(team) < numPeople && counter < totalPeople {
         // 随机选取一个小组
         //for groupIndex := 0; groupIndex < numGroups; groupIndex++ {
         groupIndex = r.Intn(numGroups)
         // 从该小组中随机选取一个人
         personIndex := r.Intn(numPeoplePerGroup)
         // 如果该小组已被使用过,则跳过
         if usedGroups[groupIndex][personIndex] {
            continue
         }

         person := groupList[groupIndex][personIndex]

         // 检查是否与已选队员来自同一小组
         if sliceContains(groupList[groupIndex], team) {
            continue
         }

         // 将该人加入队伍
         team = append(team, person)
         counter++
         // 标记该小组已使用
         usedGroups[groupIndex][personIndex] = true
         //}

      }
      // 将队伍加入结果数组
      result[i] = team
   }

   //删除最后为nil 的结果
   for len(result[len(result)-1]) == 0 {
      result = result[:len(result)-1]
   }

   //最后等于1,需要合并到两的队伍里面
   if len(result[len(result)-1]) == 1 {
      temp := result[len(result)-1]
      result = result[:len(result)-1]
      for i := range result {
         if len(result[i]) == 2 {
            // 检查是否与已选队员来自同一小组
            if sliceContains(groupList[groupIndex], result[i]) {
               continue
            }
            result[i] = append(result[i], temp...)
            break
         }
      }
   }
   // 输出结果
   var total int
   for _, team := range result {
      if len(team) > 0 {
         fmt.Println(team)
         total = +total + len(team)
      }
   }
   fmt.Println(total)
   return
}

func sliceContains(slice []string, target []string) bool {
   for _, value := range target {
      if contains(slice, value) {
         return true
      }
   }
   return false
}

func contains(slice []string, target string) bool {
   for _, value := range slice {
      if value == target {
         return true
      }
   }
   return false
}

五、拓展

如果好的算法希望大家补充呀,期待大家提出宝贵意见