划分无冲突子集问题

来源:互联网 发布:mac开机进度条卡住 编辑:程序博客网 时间:2024/05/19 11:50

问题背景:某运动会设立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);                                  //按指定大小构造一个顺序空队列Qint 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;}

在ubuntu14.04环境中,采用g++编译(g++ -std=c++11 main.cpp SqQueue.cpp),然后运行结果为集合A中的每个元素所分配组的编号:

结果:1组(0, 2, 3, 7)、2组(1, 6)、3组(4, 5)、4组(8)


参考文献:《数据结构及应用算法教程》/严蔚敏,陈文博编著.-修订本-.--北京:清华大学出版社,2011,5

0 0
原创粉丝点击