划分无冲突子集问题

问题背景:某运动会设立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;  
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值