活动选择问题

刺骨的言语ヽ痛彻心扉 2022-02-21 00:47 329阅读 0赞

题目:

有n个需要在同一天使用同一个教室的活动a1,a2,…..,an,教室同一时刻只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi 。一旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这一天。该问题就是要安排这些活动使得尽量多的活动能不冲突的举行(最大兼容活动子集)。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。

{a3,a9,a11}是一个兼容的活动子集,但它不是最大子集,因为子集{a1,a4,a8,a11}更大,实际上它是我们这个问题的最大兼容子集,但它不是唯一的一个{a2,a4,a9,a11}

20190410100259901.png

  • 动态规划算法解决思路

    我们使用 s\_\{ij\} 代表在活动 a\_\{i\} 结束之后,且 a\_\{j\} 开始之前的那些活动的集合,我们使用c[i,j] 代表 s\_\{ij\} 的最大兼容子集的大小,对上述问题就是求 c[0,12]的解。

    a, 当 i > j 或者 s\_\{ij\} 中没有任何活动元素的时候, c[i,j] = 0;

    a, 当 i < j

s\_\{ij\} 不存在活动 c[i,j] = 0;

s\_\{ij\} 存在活动的时候,c[i,j] = c[i,t] + c[t,j] + 1 , a\_\{t\} 属于 s\_\{ij\} 中的一个活动(这里是遍历 s\_\{ij\} 集合,然后求得最大兼容子集)

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. int[] s = { 0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12, 24 };
  6. int[] f = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 24 };
  7. List<int>[,] sij = new List<int>[s.Length,s.Length];
  8. for (int i = 0; i < s.Length; i++)
  9. {
  10. for (int j = 0; j < s.Length; j++)
  11. {
  12. sij[i, j] = new List<int>();
  13. }
  14. }
  15. // j s[j]用来表示第j个活动开始时间
  16. for (int j = 0; j < s.Length; j++)
  17. {
  18. // i f[i]用来表示第 i 个活动结束时间
  19. for (int i = 0; i < j; i++)
  20. {
  21. List<int> tmpSij = new List<int>();
  22. for (int n = 1; n < s.Length; n++) //n 第n个活动
  23. {
  24. if (s[n] >= f[i] && f[n] <= s[j]) //第n个活动开始时间 要大于第i个活动结束时间
  25. {
  26. tmpSij.Add(n);
  27. }
  28. }
  29. if (tmpSij.Count > 0)
  30. {
  31. int maxCount = 0;
  32. List<int> tmpList = new List<int>();
  33. foreach (int t in tmpSij)
  34. {
  35. int count = sij[i, t].Count + sij[t, j].Count + 1; // ​​​​​​​ 存在活动的时候,c[i,j] = c[i,t] + c[t,j] + 1
  36. if (maxCount < count)
  37. {
  38. maxCount = count;
  39. tmpList = sij[i, t].Union<int>(sij[t, j]).ToList<int>();
  40. tmpList.Add(t);
  41. }
  42. }
  43. sij[i, j] = tmpList;
  44. }
  45. }
  46. }
  47. List<int> l = sij[0, s.Length - 1];
  48. foreach (int item in l)
  49. {
  50. Console.WriteLine("活动:" + item);
  51. }
  52. Console.ReadKey();
  53. }
  54. }

运行结果20190410104101961.png

  • 贪心算法

想要使用贪心算法的话,得先找到适合贪心算法的规律(局部最优选择)

对于任何非空的活动集合 S ,假如 a\_\{m\} 是 S 中结束时间最早的活动,则 a\_\{m\} 一定在 S 的某个最大兼容子集中。(如何证明上面结论?反证法)

假设活动 1 在最大兼容子集中,那么就存在 最大兼容子集 =1号活动 + (1号活动结束的时间 到 总的结束时间 之间的活动子集)

假设活动 3 在最大兼容子集中,那么就存在 最大兼容子集 =3号活动 + (3号活动结束的时间 到 总的结束时间 之间的活动子集)

1号活动结束的时间 到 总的结束时间区间 > 3号活动结束的时间 到 总的结束时间区间

所以 a\_\{m\} 是 S 中结束时间最早的活动,则 a\_\{m\} 一定在 S 的某个最大兼容子集中。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTMxNjgyNA_size_16_color_FFFFFF_t_70

  1. 递归解决

    class Program
    {

    1. static int[] s = { 0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12, 24 };
    2. static int[] f = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 24 };
    3. static void Main(string[] args)
    4. {
    5. List<int> result = Activity(1,s.Length - 1,0,24);
    6. foreach (var item in result)
    7. {
    8. Console.Write(item + " ");
    9. }
    10. Console.ReadKey();
    11. }
    12. /// <summary>
    13. ///
    14. /// </summary>
    15. /// <param name="startActivity"> 开始活动编号 </param>
    16. /// <param name="endActivity"> 结束活动编号 </param>
    17. /// <param name="startTime"> 开始时间 </param>
    18. /// <param name="endTime"> 结束时间 </param>
    19. /// <returns></returns>
    20. static List<int> Activity(int startActivity, int endActivity, int startTime, int endTime)
    21. {
    22. // 当开始活动序号 > 结束活动序号 说明以及不存在可递归的活动了 或者 开始时间 >= 结束时间 没有时间进行活动了
    23. if (startActivity > endActivity || startTime >= endTime)
    24. {
    25. return new List<int>();
    26. }
    27. List<int> tmp = new List<int>();
    28. int tmpActivity = startActivity;
    29. for (int i = startActivity; i <= endActivity; i++)
    30. {
    31. if (s[i] >= startTime && f[i] < endTime)
    32. {
    33. tmpActivity = i;
    34. tmp.Add(i);
    35. break;
    36. }
    37. }
    38. return tmp.Union<int>(Activity(tmpActivity + 1, endActivity, f[tmpActivity], endTime)).ToList<int>();
    39. }

    }

运行结果:20190410114225773.png

  1. 迭代解决

    class Program

    1. {
    2. static void Main(string[] args)
    3. {
    4. int[] s = { 0, 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12, 24 };
    5. int[] f = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 24 };
    6. int startTime = 0;
    7. int endTime = 24;
    8. int startActivity = 1;
    9. List<int> resultList = new List<int>();
    10. for (int i = startActivity; i < s.Length; i++)
    11. {
    12. if (s[i] >= startTime && f[i] < endTime)
    13. {
    14. startTime = f[i];
    15. resultList.Add(i);
    16. }
    17. }
    18. foreach (var item in resultList)
    19. {
    20. Console.Write(item + " ");
    21. }
    22. Console.ReadKey();
    23. }
    24. }

发表评论

表情:
评论列表 (有 0 条评论,329人围观)

还没有评论,来说两句吧...

相关阅读

    相关 活动选择问题

    Problem Description sdut 大学生艺术中心每天都有n个活动申请举办,但是为了举办更多的活动,必须要放弃一些活动,求出每天最多能举办多少活动。 Inpu

    相关 活动选择

    活动选择 Problem Description 学校的大学生艺术中心周日将面向全校各个学院的学生社团开放,但活动中心同时只能供一个社团活动使用,并且每一个社团活动开始后

    相关 活动选择

    活动选择 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 学校的大学生艺术中心周日将面