《编程之美》1.9:高效率的安排见面会的一个解法
原题是这样的:
在校园招聘的季节里,为了能让学生们更好地了解微软亚洲研究院各研究组的情况,HR部门计划为每一个研究组举办一次见面会,让各个研究组的员工能跟学生相互了解和交流(如图1-4所示)。已知有n位学生,他们分别对m个研究组中的若干个感兴趣。为了满足所有学生的要求,HR希望每个学生都能参加自己感兴趣的所有见面会。如果每个见面会的时间为t,那么,如何安排才能够使得所有见面会的总时间最短? 最简单的办法,就是把m个研究组的见面会时间依次排开,那我们就要用m * t的总时间,我们有10多个研究小组,时间会拖得很长,能否进一步提高效率?
此题的官方解法是将问题转化为一个已知的图的问题:即图的最少着色问题。 但有两点感觉不太好:
- 一是这个问题的转换感觉角度有些大,不够平滑。 如果此前没听过图的最少着色问题,那么是怎么也不可能想到这儿的。
- 二是关于此题的分析与解法讲解非常笼统,尤其是解法,寥寥数语就完了 - 我觉得是没有把问题讲清楚的 - 虽然读者自己可以阅读“ 图的最少着色问题”来获得更多的了解,但既然此题作为单独的一题存在,我觉得还是讲清楚点好。
另外,自己思考了一下,觉的此题不转化成图的最少着色问题,通过简单的递归,应该也是可以实现的。
思路分析
已知有n位学生,m个见面会,且每个学生可以选择参加任意多个见面会。我们的目的是在没有冲突的情况下把某几个见面会的时间重叠起来,同时开。何为冲突,比如学生甲参加了见面会A与B,那么A与B就是冲突的,因为如果同时开的话,学生甲必然要放弃一个。
那么,我们可以按以下方式,逐个考虑见面会:
- 对于见面会A,因为其前面没有见面会,略过;
- 对于见面会B,考虑它和其前面的见面会A是否冲突,如果不冲突,就将B和A合并,继续考虑C;而另外还有一个分支是不管是否冲突,此时不做合并,直接考虑C;
- 对于见面会C,考虑它和其前面的见面会A,B是否冲突,第一个分支是如果与A不冲突, 就将C和A合并,继续考虑D;第二个分支是如果与B不冲突, 就将C和B合并,继续考虑D;而第三格分支还是不做任何合并,直接考虑D
- 按此规则继续对下一个见面会做考察
- 当对最后一个招聘会做完考察后,记下其时间,程序然后递归回溯,会由其他分支继续考察最后一个招聘会,比较并记录最短的那个,这样,当所有的分支都考察过后,记录的那个最短时间就是全局最短的时间了。
代码
- 输入数据可以用一个二维数组input[m][n]来表示,行表示见面会,列表示学生,数组元素表示某学生是否参加该见面会。
- 递归过程用当前考虑的见面会控制,当最后一个见面会考虑完之后,就得到一个候选解,程序然后回溯,从另一分支再次进入,考虑最后一个见面会,与之前的候选解比较,保存较优的那个:候选解包括见面会时间与当前的具体安排
![](/assets/blank.gif)
![](/assets/blank.gif)
2 #include <list>
3 #include <vector>
4
5 using namespace std;
6
7 // If 2 recruiting meetings are conflicting
8 bool IsConflict(const vector<bool>& v1, const vector<bool>& v2)
9 {
10 for(int i = 0; i < v1.size(); ++i)
11 {
12 if(v1[i] && v2[i]) return true;
13 }
14 return false;
15 }
16
17 // merge 2 recruiting meetings: v2 will be held at the same time of v1
18 void Merge(vector<bool>& v1, const vector<bool>& v2)
19 {
20 for(int i = 0; i < v1.size(); ++i)
21 {
22 v1[i] = v1[i] || v2[i];
23 }
24 }
25
26 // input: input[m][n], 2d array to represents students' selection of meetings. row stands for meetings, column stands for students,
27 // and array value stands for if a student select a meeting
28 // next: The meeting to check
29 // curTime: Time required so far
30 // curArrangement: record the information of which meeting is merged with another meeting
31 // best[output]:The best time
32 // bestArrangement[output] : The best arragement
33 void ArrangeRecruitings(vector<vector<bool> >& input, int next, int& curTime, vector<list<int> >& curArrangement, int& bestTime, vector<list<int> >& bestArrangement)
34 {
35 int m = input.size();
36 // base cases
37 if(next >= m)
38 {
39 // Save the best one
40 if(curTime < bestTime)
41 {
42 bestTime = curTime;
43 bestArrangement = curArrangement;
44 }
45
46 return;
47 }
48 else
49 {
50 // recursive cases
51 for(int i = 0; i <= next; ++i)
52 {
53
54 if(curArrangement[i].empty()) continue; // if already merged with other ones, just skip it
55
56 if (!IsConflict(input[i], input[next]))
57 {
58
59 // update the status
60 vector<bool> bkI = input[i];
61 Merge(input[i], input[next]);
62
63 curArrangement[next].pop_back();
64 curArrangement[i].push_back(next);
65
66 // Consider next one after merge
67 ArrangeRecruitings(input, next+1, curTime, curArrangement, bestTime, bestArrangement);
68
69 // restore the status
70 curArrangement[i].pop_back();
71 curArrangement[next].push_back(next);
72
73 input[i] = bkI;
74 }
75 }
76
77 // Consider next one without any merge
78 ArrangeRecruitings(input, next+1, ++curTime, curArrangement, bestTime, bestArrangement);
79
80 }
81 }
82
83
84 int main()
85 {
86 // 1. Initializing
87 int m = 3;
88 int n = 4;
89
90 vector<vector<bool> > input(m, vector<bool>(n, false));
91
92 input[0][0] = true;
93 input[0][1] = true;
94
95 input[1][1] = true;
96 input[1][2] = true;
97
98 input[2][2] = true;
99 input[2][3] = true;
100
101 //input[0][2] = true;
102
103 int curTime = 1;
104 int bestTime = m+1;
105 vector<list<int> > curArrangement(m);
106 vector<list<int> > bestArrangement(m);
107 for(int i = 0; i < m; ++i) curArrangement[i].push_back(i);
108
109 // 2. Solve
110 ArrangeRecruitings(input, 1, curTime, curArrangement, bestTime, bestArrangement);
111
112 // 3. Output the result
113 cout << "Totoal Time: " << bestTime << endl;
114 for(int i = 0; i < m; ++i)
115 {
116 cout << i << "): ";
117 if(bestArrangement[i].empty())
118 {
119 cout << "none" << endl;
120 continue;
121 }
122 for(list<int>::const_iterator it = bestArrangement[i].begin(); it != bestArrangement[i].end(); ++it)
123 cout << *it << "-";
124
125 cout << endl;
126 }
127 }
复杂度分析
可以注意到,我们需要逐个考虑见面会,一共是m个;而在考虑第i个见面会时,我们最多可能会产生出i个分支,不难看出,总问题的规模为m!;而产生每个分支时需要做的计算是O(n)的冲突检测与合并,于是,整个的算法复杂度为:O(m! * n)
《编程之美》1.9:高效率的安排见面会的一个解法相关推荐
- 编程之美读书笔记之-高效率的安排见面会
问题一: n个同学,分别对m个招聘见面会感兴趣.为了满足所有学生的要求,hr希望让每个同学都能参加自己所有感兴趣的见面会.然后每个见面会的时间为t.问如何安排见面会能够使得所有见面会总的时间最短. 建 ...
- 编程之美 - 读书笔记 - 卖书折扣问题的贪心解法
<编程之美>读书笔记(四):卖书折扣问题的贪心解法 每 次看完<编程之美>中的问题,想要亲自演算一下或深入思考的时候,都觉得时间过得很快,动辄一两个小时,如果再把代码敲一遍的话 ...
- 面试难,应聘难,好工作,今安在?——《编程之美——微软技术面试心得》为你探路!即将上市,敬请关注!
这本书是我目前所见到的优秀面试试题的最全集,包含大量有趣且有启发性的题目,一方面对于学生的指导意义重大,另一方面,即使对于我们这些已经工作的人来说,也不失为一本充满智慧与趣味的好书." ...
- java 编程之美_《编程之美—微软技术面试心得》PDF 下载
第1章 游戏之乐--游戏中碰到的题目 1.1 让CPU占用率曲线听你指挥 1.2 中国象棋将帅问题 1.3 一摞烙饼的排序 1.4 买书问题 1.5 快速找出故障机器 1.6 饮料供货 1.7 光影切 ...
- 面试难,应聘难,好工作,今安在?——《编程之美——微软技术面试心得》为你探路!即将上市,敬请关注!...
这本书是我目前所见到的优秀面试试题的最全集,包含大量有趣且有启发性的题目,一方面对于学生的指导意义重大,另一方面,即使对于我们这些已经工作的人来说,也不失为一本充满智慧与趣味的好书." -- ...
- 精选的一些《编程之美》相关资料
又要到一年的招聘季了,肯定又有很多人开始啃<编程之美>了吧.这本书从开阔视野的角度来说很好,不过限于篇幅,有的问题并没有讲清楚(甚至问题叙述模棱两可.被标榜为"鼓励同面试官交流以 ...
- 让多核CPU占用率曲线听你指挥(Windows实现)——《编程之美》1.1学习笔记
让多核CPU占用率曲线听你指挥--<编程之美>1.1学习笔记 Problem: 写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率.有以下几种情况: ...
- 编程之美-翻烙饼问题
翻烙饼问题 前言 翻烙饼问题是非常经典的问题,星期五的晚上,一帮同事在希格玛大厦附近的"硬盘酒吧"多喝了几杯.程序员多喝了几杯之后谈什么呢?自然是算法问题.有个同事说: " ...
- 《编程之美》相关参考资料
转载自:https://blog.csdn.net/wuyuegb2312/article/details/9896831 为了便于查阅,也为了方便后人不必在搜索上浪费时间,我把比较有价值的文章的链接 ...
最新文章
- vscode 中搭建Vue.js
- poj 3038 Children of the Candy Corn bfs dfs
- ST17H26 SDK中宏定义注意事项
- Linux一键安装Lnmp
- redis 安装使用
- 通过自定义配置实现插件式设计
- 使用deploy命令上传jar到私有仓库
- dom选择方法的区别
- sonarqube代码检核工具安装
- PTA10、统计字符个数 (10 分)
- java流对象_Java对象流的使用
- OpenCV初探 —— VS2019配置环境
- “我是技术总监,你为毛总问我技术细节?”
- H5网站模板——前台和后台
- 软件测试——白盒测试
- 如何根据图片找到图中的地点
- 大学生职业生涯规划书性格特征_大学生职业生涯规划书自我分析范文
- 元宇宙、区块链和潘家园
- 容量 Byte、KB、MB、GB、TB、PB、EB、ZB、YB、NB、DB、CB、XB
- 数字图像处理实验(七)| 形态学图像处理{生成结构元素strel、腐蚀运算imerode、膨胀运算imdilate、开运算imopen、闭运算imclose}(附代码和实验截图、汉字视力表项目、总结)
热门文章
- c语言两点间距离_数字图像处理|P5第二章 数字图像基础第四节像素间的基本关系...
- 利用函数重载编写函数max_c++笔记(函数重载)
- grub2配置原理分析
- 计算机考研去哪个城市,2019计算机考研:考研热门城市院校排名
- Ansible Synchronize
- python log
- 在过程中要正式批准可交付成果_PMP模拟考试一(200题中文版)
- 4安全框_压花玻璃与安全玻璃有哪些特点?玻璃隔断的介绍
- 基于物联网的新型智能家居控制系统设计
- CVE-2020-1472复现与完整利用