• 会场安排问题


    问题描述:

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

    算法设计

    对于K个待安排的活动,计算使用最少会场的时间表。

    输入输出

    input.txt 

    1 23 
    12 28 
    25 35 
    27 80 
    36 50 
    output.txt 
    3

    先简单描述我的思路,可能很多人刚开始和我的思路一样,最终代码很丑,不过还是简单叙述一下思路,首先从第一个开始的活动出发,根据该活动的结束时间找到在这个时间之后的第一个活动的开始时间,然后从这个新活动出发再去寻找下一个第一个出现的相容的活动,依次类推,结束第一次遍历,可以找到一个会场的所有活动,这些活动标记一下,下次不再遍历。开启下次遍历,再找出一个新的会场的所有的活动,直到所有的活动找到会场为止。代码如下,只可以做参考(太丑)。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define M 100
     4 int array1[M];
     5 int array2[M];
     6 void arange(int array1[M],int array2[M],int n){
     7     int array3[M];
     8     memset(array3,0,sizeof(array3));//作用是标记该活动是否已经找到会场。
     9     int maxnum=0;//需要的会场的个数
    10     int t=0;//记录当前活动的序号
    11     int s=0;//每个会场的活动个数
    12     int counts=0;//记录总共标记的活动数
    13     while(true){
    14         s=0;//开始新的一个会场的活动
    15         t=0;
    16         for(int i=t;i<n;i++){
    17            if(array3[i]==1) continue;//已标记的活动直接跳过
    18                 for(int j=i;j<n;j++){//寻找下一个相容的活动,找到就break
    19                      s++;//遍历活动,加一
    20                     if(s==1){//如果是第一个没归入会场的活动标记下来,归入新会场的第一个活动
    21                         array3[i]=1;
    22                         counts++;//已经加入活动的数目加一
    23                     }
    24                 if(array2[i]<array1[j] && array3[j]==0){//得到下一个相容的活动。,归入新的会场,结束循环
    25                     array3[j]=1;
    26                     t=j;//更新归入的活动的坐标,下次从这个活动向后找
    27                     counts++;//已经加入会场的数目加一
    28                     break;
    29                 }
    30         }
    31         }
    32         maxnum++;//会场的数目加一
    33         if(counts==n){//所有活动处理完,结束
    34             break;
    35         }
    36     }
    37     cout << maxnum;
    38 }
    39 int main()
    40 {
    41     int n;
    42     cin >> n;
    43     memset(array1,0,sizeof(array1));//用户输入的活动的起始点
    44     memset(array2,0,sizeof(array2));//用户输入的活动的结束时间
    45     for(int i=0;i<n;i++){
    46         cin >> array1[i] >> array2[i];
    47     }
    48     int array3[n];
    49     int array4[n];
    50     memset(array3,0,sizeof(array3));//存储排序后的活动的起始点
    51     memset(array4,0,sizeof(array4));//存储排序后的活动的终止时间点
    52      for(int i=0;i<n;i++){//复制一下起始时间,
    53         array3[i]=array1[i];
    54     }
    55     sort(array3,array3+n);//所有的活动根据起始时间进行排序用array3和array4进行存储
    56       for(int i=0;i<n;i++){
    57         for(int j=0;j<n;j++)
    58         {
    59             if(array1[j]==array3[i]){
    60                 array4[i]=array2[j];
    61             }
    62         }
    63     }
    64       arange(array3,array4,n);
    65 }

    下面是书上的参考代码,很精简,代码稍微有点不是好理解,但是思路和我的方法基本一致

    具体的思路是:看一活动的开始时间是否大于某一会场的结束时间,如果是就加入该会场中,并更新该会场的结束时间,(如果有多个会场选择的话,就选最优的那一个,即结束时间最小的)
    ,如果所有的会场都不满足,则再新增一个会场,把该活动的结束时间赋予这个新增的会场。
    算法的时间复杂度: 设有 n 个活动,使用了k(k<=n)个会场,则时间复杂度为 O(n(k+nlogn))

     1 #include <iostream>
     2 #include <vector>
     3 #include <algorithm>
     4 using namespace std;
     5 struct point
     6 {
     7     int t;//活动的端点时间
     8     bool f;//true表示活动开始false标志活动结束时间
     9 };
    10 bool cmp(point x,point y)//比较时间点的大小
    11 {
    12     return x.t<y.t;
    13 }
    14 
    15 int greedy(vector<point> x)
    16 {
    17     int max=0,cur=0,n=x.size();
    18     sort(x.begin(),x.end(),cmp);//从小到大排序一下各个活动时间点,排序规则是cmp
    19     for(int i=0;i<n;i++)
    20     {
    21         if(x[i].f)//如果是活动开始时间则加一
    22             cur++;
    23         else//结束时间减一
    24             cur--;
    25         if((i==n-1 || x[i].t<x[i+1].t) && cur>max)//此时间点有多个活动进行,而这个重叠活动最大值就是需要的最大会场数
    26             max=cur;                             
    27     }                                              
    28     return max;                                   
    29 }
    30 
    31 int main()
    32 {
    33     vector<point> x;//容器
    34     int n,i;
    35     point temp;
    36     while(cin>>n,n)//输入n个活动,即2n个point
    37     {
    38         for(i=0;i<n;i++)
    39         {
    40             temp.f=true;
    41             cin>>temp.t;
    42             x.push_back(temp);//活动开始时间,标志temp.f为true,进入容器
    43             temp.f=false;
    44             cin>>temp.t;
    45             x.push_back(temp);//活动结束时间,标志temp.f为false,进入容器
    46         }
    47         cout<<greedy(x)<<endl;
    48         x.clear();//clear只是把那些元素全部删除掉,并不是释放内存
    49     }
    50     return 0;
    51 }

     

  • 相关阅读:
    LeetCode 23. 合并K个排序链表
    LeetCode 199. 二叉树的右视图
    LeetCode 560. 和为K的子数组
    LeetCode 1248. 统计「优美子数组」
    LeetCode 200. 岛屿数量
    LeetCode 466. 统计重复个数
    LeetCode 11. 盛最多水的容器
    LeetCode 55. 跳跃游戏
    LeetCode 56. 合并区间
    Java生鲜电商平台-订单架构实战
  • 原文地址:https://www.cnblogs.com/henuliulei/p/9961337.html
Copyright © 2020-2023  润新知