天天看点

会场安排问题贪心算法

问题描述:

假设要在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的贪心算法进行安排(这个问题实际上是著名的图着色问题。若将每一个活动作为图的一个顶点,不相容活动间用边相连。使相邻顶点着有不同颜色的最小着色数,相应于要找的最小会场数)。

问题解答:

<1>、用贪心选择策略解会场安排问题。

贪心算法重要的两个性质:贪心选择性质和最优子结构性质。

1、 问题的贪心选择性质

证明:首先将会场安排问题数学化,设有n个活动的集合 e= { 1 ,2 ,…,n },每个活动 i 都有一个要求使用该会场的起始时问si 和一个结束时问fi 。即k是所需最少会场的个数。设活动已排序,( a1 , a2 , … ,ak )是所需要的k个已安排了活动的会场。①当k = 1时,也就是所有的活动在一个会场里相容,a1 是满足贪心选择性质的最优解;②当k>= 2时,取b1=a1,bk=ak (即bk是安排了m个活动的一个会场,(n-m)个活动都安排在b1 到bk-1个会场里)。就是(n-m)个活动安排需要(k -1)个会场。则(b1,b2 ,…,bk-1 )是(n - m)个活动可行解。另一方面,由{( a1 ,a2,…,ak)-ak}=(b1,b2,…,bk-1)知,(b1,b2,…,bk-1)也是满足贪心选择性质的最优解,所以,会场安排问题具有贪心选择性质。

2、 问题的最优子结构性质

证明:( a1,a2, …,ak )是n个活动的集合e= {1,2 ,…,n }所需会场的最优解。设a1中安排了m个相容的活动,那么也就是说(n-m)个活动完全安排需要k-1个会场。假设(n - m)个活动安排只需要k-2个会场或则更少的会场。也就是说n个活动安排只需要k-1个会场或者更少的会场就可以安排完,则前后出现矛盾。一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

<2>、算法实现思路:

1)、首先定义一个时间类Node,有两个data,flag属性,其中data是时间值,flag是判断活动开始还是结束。

2)、对所有的时间对象按.data排序到一个数组中,其中选择归并排序方法进行排序。

3)、该算法的贪心选择的意义是使剩余的可安排时间段极大化,以便安排尽可能多的相容活动。依次取出该数组的数据,判断其属性flag,若是一个开始时间,则Count++,若是结束时间Count--,其中最大的Count值就是要求的最少的会场数。

<3>、算法程序:

#include<iostream>
#include<fstream>
using namespace std;

//活动的时间类
class Node
{
public:
    int data;//时间值
    bool flag;//判断是开始还是结束,0表示开始,1表示结束
    bool operator<(Node &secondRational);//按时间值比较两个对象的大小
};

//比较类中时间值的大小
bool Node::operator<(Node &secondRational)
{
    if((this->data-secondRational.data)<0)
        return true;
    else
        return false;
}

//复制数组函数
template<typename T>
void arraycopy(T source[],int sourceStartIndex,T target[],int targetStartIndex,int length);
//合并数组函数
template<typename T>
void merge(T list1[],int list1Size,T list2[],int list2Size,T temp[]);
//归并排序函数
template<typename T>
void mergeSort(T list[],int arraySize)
{
    if(arraySize>1)
    {
        //复制排序前半部分数组
        T *firstHalf=new T[arraySize/2];
        arraycopy(list,0,firstHalf,0,arraySize/2);
        mergeSort(firstHalf,arraySize/2);

        //复制排序后半部分数组
        int secondHalfLength=arraySize-arraySize/2;
        T *secondHalf=new T[secondHalfLength];
        arraycopy(list,arraySize/2,secondHalf,0,secondHalfLength);
        mergeSort(secondHalf,secondHalfLength);

        //合并复制两部分数组
        T *temp=new T[arraySize];
        merge(firstHalf,arraySize/2,secondHalf,secondHalfLength,temp);
        arraycopy(temp,0,list,0,arraySize);

        delete []temp;
        delete []firstHalf;
        delete []secondHalf;
    }
}

//将两个数组按大小顺序合并入一个数组中
template<typename T>
void merge(T list1[],int list1Size,T list2[],int list2Size,T temp[])
{
    int current1=0;
    int current2=0;
    int current3=0;

    while(current1<list1Size&¤t2<list2Size)
    {
        if(list1[current1]<list2[current2])
            temp[current3++]=list1[current1++];
        else
            temp[current3++]=list2[current2++];
    }

    while(current1<list1Size)
        temp[current3++]=list1[current1++];
    while(current2<list2Size)
        temp[current3++]=list2[current2++];
}

//将一个数组复制到另一个数组中
template<typename T>
void arraycopy(T source[],int sourceStartIndex,T target[],int targetStartIndex,int length)
{
    for(int i=0;i<length;i++)
    {
        target[i+targetStartIndex]=source[i+sourceStartIndex];
    }
}

//最少会场数函数
int Greedyplan(Node f[],int n)
{
    int Count=0;
    int maxCount=0;
    /*
    遍历活动时间,若此时间为开始时间,则Count+1,为结束时间,则Count-1
    统计得出最大的Count值,并将Count值赋给maxCount,
    此maxCount值就为最少的会场数
    */
    for(int i=0;i<n;i++)
    {
        if(f[i].flag==0)//若此时间为开始时间Count+1
        {
            Count++;
            if(Count>maxCount)//记录次循环中最大的Count值
            {
                maxCount=Count;
            }
        }
        else//若为结束时间Count-1
        {
            Count--;
        }
    }
    return maxCount;
}

int main()
{
    //读出输入文件中的数据
    fstream fin;
    fin.open("input.txt",ios::in);
    if(fin.fail())
    {
        cout<<"File does not exist!"<<endl;
        cout<<"Exit program"<<endl;
        return 0;
    }

    int n;
    fin>>n;

    //建立两个Node类型的数组,用于存放开始时间和结束时间
    Node *a=new Node[n];
    Node *b=new Node[n];
    for(int i=0;i<n;i++)
    {
        fin>>a[i].data;
        fin>>b[i].data;
    }

    //将开始时间表示为0
    for(int j=0;j<n;j++)
    {
        a[j].flag=0;    
    }
    //将结束时间表示为1
    for(int k=0;k<n;k++)
    {
        b[k].flag=1;    
    }

    //再建立一个Node类型的数组,将前两个数组中的数据复制到此数组中
    Node *c=new Node[2*n];
    arraycopy(a,0,c,0,n);
    arraycopy(b,0,c,n,n);

    //将数组c按时间值大小排序,此排序为稳定的归并排序
    mergeSort(c,2*n);

    //调用最少会场数函数
    int mm=Greedyplan(c,2*n);
    //控制台输出
    cout<<"最少会场数为:"<<mm<<endl;

    //将结果数据写入到输出文件
    fstream fout;
    fout.open("output.txt",ios::out);
    fout<<mm;

    fin.close();
    fout.close();
    system("pause");
    return 0;
}
           

继续阅读