划分无冲突子集问题

来源:互联网 发布:八大网络虚拟歌姬 编辑:程序博客网 时间:2024/06/05 19:04

问题背景:某运动会设立N个比赛项目,每个运动员可以参加1-3个项目。试问如何安排比赛日程,既可以使同一运动员参加的项目不安排在同一单位时间进行,又使总的竞赛日程最短。

问题抽象:若将此问题抽象成数学模型,则归属于“划分子集”问题。N个比赛项目构成一个大小为n的集合,有同一运动员参加的项目则抽象为“冲突”关系。

解决问题:假设某运动会设有9个项目,A = {0, 1, 2, 3, 4, 5, 6, 7, 8},7名运动员报名参加的项目分别为:(1, 4, 8)、(1, 7)、(8, 3)、(1, 0, 5)、(3, 4)、(5, 6, 2)和(6, 4),则构成一个冲突关系的集合R = { (1, 4), (4, 8), (1, 8), (1, 7), (8, 3), (1, 0), (0, 5), (1, 5), (3, 4), (5, 6), (5, 2), (6, 2), (6, 4)}(一对括号中的两个项目不能安排在同一单位时间)。“划分子集”问题即将集合A划分成k个互不相交的子集A1, A2, ... , Ak(k <= n),使同一子集中的元素均无冲突关系。

解决方法:利用队列先进先出的特点,将待划分的集合A中的所有元素放入一个队列中,然后依次取出元素放入一个待分配的组中,若当前元素与改组中已经入选的元素无冲突,则出栈,如果产生冲突则继续放在队列的尾部;当遍历考察一轮队列中的所有元素后,产生一个无冲突的子集,如此循环直到所有元素都被分配完成时结束分配。


源文件:main.cpp(重点在于子函数DividSubset的实现) SqQueue.h SqQueue.cpp


main.cpp文件:

#include <iostream>  #include "SqQueue.h"    #define ITEM 9                                                                //ITEM宏表示待处理集合中元素的项数    using namespace std;    int R[ITEM][ITEM] = {                                                      //该矩阵用来存储待划分集合中元素之间的冲突关系,0代表不冲突,1代表冲突  {0, 1, 0, 0, 0, 1, 0, 0, 0},  {1, 0, 0, 0, 1, 1, 0, 1, 1},  {0, 0, 0, 0, 0, 1, 1, 0, 0},  {0, 0, 0, 0, 1, 0, 0, 0, 1},  {0, 1, 0, 1, 0, 0, 1, 0, 1},  {1, 1, 1, 0, 0, 0, 1, 0, 0},  {0, 0, 1, 0, 1, 1, 0, 0, 0},  {0, 1, 0, 0, 0, 0, 0, 0, 0},  {0, 1, 0, 1, 1, 0, 0, 0, 0}};    int result[ITEM];                                                               //该数组用来存放分组后的结果    /***********************************************************  *功能:划分给定集合的无冲突子集  *输入:集合中元素关系集,集合中基本元素的个数,存储结果的数组  *时间:2016年10月17日  ***********************************************************/  void DivideSubset(int R[][ITEM], int n, int result[])  {          int PreIndex = n, GroupIndex = 0;                       //PreIndex表示前一次出队列的元素序号,GroupIndex表示当前分配的组的编号          SqQueue SQ;          InitQueue(SQ);                                                       //初始化一个队列,长度与被划分集合的基本元素个数相同,本例中取9          for(int i = 0 ; i < n ; ++i)                                        //该循环用来给分配的队列附上初值,该例中为(0-8)          {                  EnQueue(SQ, i);          }          int currVal;                                                            //该变量用来表示当前待考察的队列中的一个元素          int clash[ITEM];                                                     //该数组用来表示当前分配组的已经添加的元素与其它元素的关系,即是否产生冲突          while(!QueueEmpty(SQ))                                       //该循环用来处理队列中的每一个元素,知道所有元素都分配完成时结束          {                  DeQueue(SQ, currVal);                                   //取出一个元素进行处理                  if(currVal <= PreIndex)                                  //如果当前元素小于前一个,则表示队列已经循环遍历所有的元素,应该新建另一个组                  {                          ++GroupIndex;                          for(int i = 0 ; i < n ; ++i)                                  clash[i] = 0;                  }                  if(clash[currVal] == 0)                                   //查询当前分配组的clash数组的值,当值为0时表示该元素没有与当前组中已经添加的元素产生冲突                  {                          result[currVal] = GroupIndex;              //将当前元素编入该组                          for(int i = 0 ; i < n ; ++i)                       //添加与被添加元素冲突的信息                                  clash[i] += R[currVal][i];                  }                  else                  {                          EnQueue(SQ, currVal);                          //如果该元素与当前组中的所有元素都冲突,将该元素继续入栈                  }                  PreIndex = currVal;          }  }    int main()  {          DivideSubset(R, ITEM, result);                    for(const auto &e : result)          {                  cout << e << " ";          }          cout << endl;                    return 0;  }  

SqQueue.h文件:
#ifndef SQQUEUE_H  #define SQQUEUE_H    #define MAXQSIZE 10                                                     //队列的最大长度    typedef int QElemType;    typedef struct  {          QElemType *base;                                                //初始化的动态分配存储空间          int front;                                                              //头指针,若队列不空,指向队列头元素          int rear;                                                               //尾指针,若队列不空,指向队列尾元素的下一个位置  }SqQueue;    bool InitQueue(SqQueue &Q);                                  //按指定大小构造一个顺序空队列Q  int QueueLength(SqQueue &Q);                               //返回Q的元素的个数,即队列长度  bool EnQueue(SqQueue &Q, QElemType e);            //插入元素e为Q的新的队尾元素  bool DeQueue(SqQueue &Q, QElemType &e);         //对头元素出队列  void PrintSqQueue(SqQueue &Q);                           //打印顺序队列  bool QueueEmpty(SqQueue &Q);                            //判断给定的队列是否为空队列    #endif  /* SqQueue.h */  

SqQueue.cpp文件:
#include "SqQueue.h"  #include <stdlib.h>  #include <iostream>    using namespace std;    bool InitQueue(SqQueue &Q)  {          Q.base = (QElemType*)malloc(MAXQSIZE*sizeof(QElemType));          if(Q.base == nullptr)                  return false;          Q.front = Q.rear = 0;          return true;  }    int QueueLength(SqQueue &Q)  {          return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;  }    bool EnQueue(SqQueue &Q, QElemType e)  {          if(((Q.rear + 1) % MAXQSIZE) == Q.front)                  return false;          Q.base[Q.rear] = e;          Q.rear = (Q.rear + 1) % MAXQSIZE;          return true;  }    bool DeQueue(SqQueue &Q, QElemType &e)  {          if(Q.rear == Q.front)                  return false;          e = Q.base[Q.front];          Q.front = (Q.front + 1) % MAXQSIZE;          return true;  }    void PrintSqQueue(SqQueue &Q)  {          int i = Q.front;          while(i != Q.rear)          {                  cout << Q.base[i] << " ";                  i = (i + 1) % MAXQSIZE;          }          cout << endl;  }    bool QueueEmpty(SqQueue &Q)  {          return (Q.front == Q.rear) ? true : false;  }  


原创粉丝点击