编程之美:小飞的电梯调度算法

一.问题描述 

  亚洲微软研究院所在的希格玛大厦一共有6部电梯。在高峰时间,每层都有人上下,电梯每层都停。实习生小飞常常会被每层都停的电梯弄的很不耐烦,于是他提出了这样一个办法: 由于楼层并不算太高,那么在繁忙的上下班时间,每次电梯从一层往上走时,我们只允许电梯停在其中的某一层。所有乘客从一楼上电梯,到达某层后,电梯停下来,所有乘客再从这里爬楼梯到自己的目的层。在一楼的时候,每个乘客选择自己的目的层,电梯则计算出应停的楼层。

  问:电梯停在哪一层楼,能够保证这次乘坐电梯的所有乘客爬楼梯的层数之和最少?

二.算法描述  

  方法一:暴力枚举,时间复杂度O(N^2)

 1 /*
 2  * O(N^2)
 3  */
 4 const int N = 5;
 5 int num[N+1];
 6 int ans;
 7 int min = INT_MAX;
 8
 9 for(i = 1;i<=N;i++)
10 {
11     int sum = 0;
12     for(j = 1;j<=N;j++)
13     {
14         sum += num[j] * abs(j - i);
15     }
16     if(sum < min)
17     {
18         min = sum;
19         ans = i;
20     }
21     return (ans);
22 }

  

  方法二:书上提供的O(N)的动态规划的算法。

  假设电梯停在i层楼,可以计算出所有乘客要爬楼层的层数为Y,假设此时有N1个乘客在i层楼以下,N2个乘客在I层楼,N3个乘客在I层楼以上,则当电梯停在i+1层的时候,N1+N2个乘客要多下一层楼,共多下N1+N2层,N3个乘客要少往上面爬一层楼,少上N3层楼,此时Y(i+1) = Y(i) + N1+N2-N3,很显然,当N1+N2<N3的时候,Y不断减小。Y1很容易算出来,另外我们还可以看出,N1+N2是递增的,N3是递减的,所以N1+N2一旦大于N3的时候,我们直接退出循环即可,没有必要再计算下去了。

  

 1 /*
 2   O(N)  dp
 3  */
 4 #include <iostream>
 5 #include <cstring>
 6 using namespace std;
 7
 8 const int N = 5;
 9 int num[N+1];
10
11 int solve()
12 {
13     int ans = 1;
14     int min = 0;
15     int N1 = 0,N2 = num[1],N3 = 0;
16     for(int i = 2;i<=N;i++)
17     {
18         //由于只要最小值即可,所以不必用数组保存;使用数组时不用知道N1+N2 < N3)就行
19         min += num[i] * (i-1);
20         N3 +=num[i];
21     }
22
23     for(int i = 2;i<=N;i++)
24     {
25         if(N1+N2 < N3)
26         {
27             ans = i;
28             min +=(N1+N2-N3);
29
30             N1 +=N2;
31             N2 = num[i];
32             N3 -=num[i];
33         }
34         else
35             break;
36     }
37     return ans;
38 }
39
40 int main()
41 {
42     int i,j,k;
43     num[1] = 6;
44     num[2] = 2;
45     num[3] = 4;
46     num[4] = 9;
47     num[5] = 8;
48     int ans = solve();
49     cout<<ans<<endl;
50     while(1);
51     return 0;
52 }
53     

  方法三:中位数

  其实这道题目仔细分析起来就是求一组数据的中位数而已。假设两人,分别到3层楼和8层楼下,在3和8之间取一点,使得到两个点距离最小,很显然,在3和8中的每一点到3和8的距离之和都是相等的。推广到2 3

5 5 6 7 8 8 9这样一组数据,ans为中位数。

数组a存储下标,数组b存储相应人数,按b从小到大排序并交换a(或者用结构体),最后的中位数是不对的

  

 1 /*
 2  * mid_value
 3  */
 4  // 这种方法貌似是对的
 5 #include <iostream>
 6 #include <cstring>
 7 using namespace std;
 8
 9 const int N = 5;
10 int num[N+1];
11
12 int solve()
13 {
14     int left = 1,right = N;
15     while(right-left >= 1)
16     {
17         while(num[left] == 0)left++;
18         num[left] --;
19         while(num[right] == 0)right--;
20         num[right] --;
21     }
22     return left;
23 }
24
25 int main()
26 {
27     int i,j,k;
28     num[1] = 6;
29     num[2] = 2;
30     num[3] = 4;
31     num[4] = 9;
32     num[5] = 8;
33     int ans = solve();
34     cout<<ans<<endl;
35     while(1);
36     return 0;
37 }

三.结束语 

  扩展问题:往上爬一层要耗费K个单位的能量,往下走耗费1个单位的能亮,只需要计算N1+N2-N3变成N1+N2-N3*K即可。其余的都是一样的。

  扩展问题:2个有序数组求合并后的中位数,貌似是算法导论上的,有空再写……

   你让我安心什么什么,说话却吞吞吐吐,岂非存心让我不安

时间: 2024-09-24 04:33:31

编程之美:小飞的电梯调度算法的相关文章

编程之美:找到符合条件的整数

一.问题描述 任意给定一个正整数N,求一个最小正整数M(M>1),使得N*M的十进制形式只含1和0. 比如 N=99,M=1 122 334 455 667 789 ,N*M=111 111 111 111 111 111; M就是当N=99时,符合条件的数 二.解题思路 考虑将问题转化为:找只含有0和1的能被N整除的最小正整数.可以看出这是和原问题等价的. 那么需要将0和1组成的所有数从小到大遍历吗? 这样的话,如果寻找的数是k位,则需要搜索2k-1个数才能得到结果. 这里采用的方式是在计算中

编程之美:1的数目

一.问题描述 给定一个十进制数N,写下从1开始,到N的所有整数,然后数一下之中所有"1"的个数. 例如: N=12,(1,2,3,4,5,6,7,8,9,10,11,12)共有5个1 二.解题思想 假设N=abcde为一个整数,a,b,c,d,e分别对应十进制数,如果要计算(1到N)百位出现1的个数,他将受三个因素的影响:百位以上的数,百位数和百位一下的数,具体依赖如下: 分别设整数N百位以上,百位和百位一下的数字分别为:preNum,curNum,proNum,如N=abcde的三个

编程之美:24点游戏

一.问题描述 给玩家4张牌,每张牌牌面值在1~13之间,允许其中有数值相同的牌.采用加.减.乘.除四则运算,允许中间运算存在小数,并且可以使用括号,但每张牌只能使用一次,尝试构造一种表达式,使其运算结果为24. 如 输入:3 3 7 7 输出:(((3)/(7))+(3))*(7) 二.程序实现原理 遍历所有可能的组合,直到找到一个符合条件的组合或者遍历所有情况没有找到满足的组合,具体详见代码注释 三.程序基本实现 #include<iostream> #include<string&g

编程之美:平面最近点对

一.概念引入         最接近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小.严格地说,最接近点对可能多于1对.为了简单起见,这里只限于找其中的一对.         最简单的就是直接暴力,也可以分治,使用分治的话关键是如何合并,如果两边都是n/2个点比较的话,合并的时间是O(n^2),那么T(n)=2T(n/2)+O(n2),它的解为T(n)=O(n2),还是没什么优势,这就引导我们去优化合并算法.         为了找到一个有效的合并算

盖茨扎克伯格现身说法:讲述编程之酷

新浪科技讯 北京时间3月2日上午消息,美国公益组织Code.org的一段最新视频请来了马克·扎克伯格(Mark Zuckerberg)和比尔·盖茨(Bill Gates)等业界大佬现身说法,讲述编程之酷.视频:盖茨扎克伯格等讲述编程之酷 媒体来源:新浪科技这段视频不仅展示了Facebook的前卫工作环境,还邀请了迈阿密热火队的克里斯·波什(Chris Bosh)和黑眼豆豆首脑人物Will.i.am讲述编程之美:程序员已经不再是西装革履的码农,他们可以穿着套头衫在Facebook园区内东奔西跑,而

iOS开发:多线程编程之NSThread的使用详解

  1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue的使用) 3.GCD 全称:Grand Central Dispatch( iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的. 这篇我们主要介绍和使用NSThr

Python中线程编程之threading模块的使用详解

  这篇文章主要介绍了Python中线程编程之threading模块的使用详解,由于GIL的存在,线程一直是Python编程中的焦点问题,需要的朋友可以参考下 threading.Thread Thread 是threading模块中最重要的类之一,可以使用它来创建线程.有两种方式来创建线程:一种是通过继承Thread类,重写它的run方法;另一种是创建一个threading.Thread对象,在它的初始化函数(__init__)中将可调用对象作为参数传入.下面分别举例说明.先来看看通过继承th

ruby元编程之method

  这篇文章主要介绍了ruby元编程之method_missing的一个使用细节,本文介绍在使用method_missing时造成死循环的一个现象,需要的朋友可以参考下 我们知道顶级域,定义域的self是啥? 代码如下: puts self #main puts self.class #Object 我们知道当一个方法被调用的时候,如果没有对象接受,默认就是self,如: 代码如下: def tell_me_who puts self end tell_me_who #main 方法调用是这样的

Node.js 异步编程之 Callback介绍(一)

 这篇文章主要介绍了Node.js 异步编程之 Callback介绍(一),本文用实例讲解Callback的相关知识,本文是第一篇,下一篇小编会跟进,需要的朋友可以参考下     Node.js 基于 JavaScript 引擎 v8,是单线程的.Node.js 采用了与通常 Web 上的 JavaScript 异步编程的方式来处理会造成阻塞的I/O操作.在 Node.js 中读取文件.访问数据库.网络请求等等都有可能是异步的.对于 Node.js 新人或者从其他语言背景迁移到 Node.js