现在的位置: 首页 > 综合 > 正文

编程之美:第一章 1.8电梯调度算法

2018年05月18日 ⁄ 综合 ⁄ 共 2828字 ⁄ 字号 评论关闭
/*
电梯调度算法:
微软有6部电梯,每层都有人上下,电梯在每层都停。
每次电梯从一层往上走时,只允许电梯停在其中的某一层。所有的乘客都从一楼上电梯,到达某层后,电梯停下来,
所有乘客再从这里爬楼梯到自己的目的层。在一楼的时候,每个乘客选择自己的目的层,电梯则自动计算出应停在
的楼层。
电梯停在那一层,能够保证这次乘坐电梯的所有乘客爬楼梯的层数之和最少。

分析:本质是优化问题。如何寻找抽象模型。树?图?

影响结果的两个因素:乘客数目及需要停的目的楼层。我们可以从统计到达各层的乘客数目分析;
假设楼层一共有N层,电梯停在第x层,要去第i层的乘客数目总数为Tot[i],这样所爬楼梯的总数就是
从1到N 对Tot[i]*|i-x|求和
我们只需要找到x即可

解法1:
可以从第一层开始枚举x一直到N层,计算出总共要爬的层数,然后选取最小值。每层楼计算时,要把所有N层楼计算
一遍,时间复杂度为O(n),总共计算N层楼,因此总时间复杂度为O(n^2)


解法2:
假设电梯停在第i层楼,可以计算出所有乘客总共要爬楼梯的层数Y,如果有N1个乘客的目的楼层在i层楼一下,有N2
个乘客在第i层楼,还有N3个乘客再第i层楼以上。这个时候,如果电梯改停在第i-1层,所有目的地在第i-1层及
以下的乘客可以少爬一层,总共可以少爬N1层,而目的地在第i层及以上的乘客需要多爬一层,总共多爬N2+N3层
乘客总共需要爬的层数为Y-N1+(N2 + N3) = Y - (N1 - N2 - N3)
反之,如果电梯在i+1层停,乘客需要爬的层数为Y+N1+N2-N3

停i-1:Y - N1 + N2 + N3,当N1 > N2 + N3时,停在第i-1层好,乘客少走N1-N2-N3
停i+1:Y - N3 + N1 + N2,当N1 + N2 < N3时,停在第i+1层好,
其他情况:停在第i层好
我们从第一层开始考察,计算各位乘客需要爬的数目,然后调整,时间复杂度降为O(N)

 

输入:
5(电梯层数,接下来有n行,第i行的数字表示第i层有多少人想停在这一楼,电梯标号默认从1开始,如果有多层相同,就输入标号最小的)
3 5 8 9 4
4 
20 2 3 4
4
10 2 8 4
输出:(第几层电梯,以及需要怕露底的数目)
3 28
1 20
2 26
分析:
第一层停的话,那么第二层的人要走的层数:5,第三层:2*8,四:3*9,五:4*4 ,一共 = 5 + 16 + 27 + 16 = 64
第二层停:一:3,三:8,四:2*9,五:3*4 =,共 = 3 + 8 + 18 + 12 = 41
第三层停:一:2*3,二:5,四:9,五:2*4,一共 = 6 + 5 + 9 + 8 = 28
第四层停:共= 3*3 + 2 *5 + 1*8 + 4*1 = 9 + 10 + 8 + 4 = 31
第五层停:共= 4*3 + 3*5 + 2*8 + 1*9 = 12 + 15 + 16 + 9 = 52
*/

/*
关键:
1 假设电梯停在第i层楼,可以计算出所有乘客总共要爬楼梯的层数Y,如果有N1个乘客的目的楼层在i层楼一下,有N2
个乘客在第i层楼,还有N3个乘客再第i层楼以上。这个时候,如果电梯改停在第i-1层,所有目的地在第i-1层及
以下的乘客可以少爬一层,总共可以少爬N1层,而目的地在第i层及以上的乘客需要多爬一层,总共多爬N2+N3层
乘客总共需要爬的层数为Y-N1+(N2 + N3) = Y - (N1 - N2 - N3)
反之,如果电梯在i+1层停,乘客需要爬的层数为Y+N1+N2-N3

停i-1:Y - N1 + N2 + N3,当N1 > N2 + N3时,停在第i-1层好,乘客少走N1-N2-N3
停i+1:Y - N3 + N1 + N2,当N1 + N2 < N3时,停在第i+1层好,
其他情况:停在第i层好
我们从第一层开始考察,计算各位乘客需要爬的数目,然后调整,时间复杂度降为O(N)
2  for(i = 2,n1 = 0 ,n2 = pArr[1] , n3 = 0; i <= iLen ; i++)
  //用第一层开始对第1层下面,第一层,第一层上面的人数赋值
 {
  n3 += pArr[i];
  iMin += pArr[i]*(i-1);
 }
3 //需要向上一层楼梯遍历的情况是:第i-1层和第i层都需要加走的步数需要累加:N1+N2,而第i层以上
  //减少步数为:N3,如果N3> N1 + N2,此时应向上走
  if(n3 > n1 + n2)
  {
   iMin -= (n3 - n1 - n2);
   n1 += n2;
   n3 -= pArr[i];//这里必须减去当前层数
*/

#include <stdio.h>
#include <iostream>

using namespace std;

const int INT = 0x7fffffff;
const int MAXSIZE = 10000;

int abs(int n)
{
 return n > 0 ? n : -1*n; 
}

pair<int,int> elevatorAssign(int* pArr,int iLen)
{
 int iMin = INT;
 int iMinIndex = -1;
 int iSum;
 for(int i = 1 ; i <= iLen ; i++)
 {
  iSum = 0;
  for(int j = 1 ; j <= iLen ; j++)
  {
   iSum += pArr[j]*abs(i - j);
  }
  if(iSum < iMin)
  {
   iMin = iSum;
   iMinIndex = i;
  }
 }
 return make_pair<int,int>(iMinIndex,iMin);
}

pair<int,int> elevatorAssign_optimized(int* pArr,int iLen)
{
 int n1,n2,n3,i;
 int iMin = 0;
 for(i = 2,n1 = 0 ,n2 = pArr[1] , n3 = 0; i <= iLen ; i++)
  //用第一层开始对第1层下面,第一层,第一层上面的人数赋值
 {
  n3 += pArr[i];
  iMin += pArr[i]*(i-1);
 }
 for(i = 2 ; i <= iLen ; i++)
 {
  //需要向上一层楼梯遍历的情况是:第i-1层和第i层都需要加走的步数需要累加:N1+N2,而第i层以上
  //减少步数为:N3,如果N3> N1 + N2,此时应向上走
  if(n3 > n1 + n2)
  {
   iMin -= (n3 - n1 - n2);
   n1 += n2;
   n3 -= pArr[i];//这里必须减去当前层数
   n2 = pArr[i];
  }
  else
  {
   break;
  }
 }
 return make_pair<int,int>(i-1,iMin);
}

void process()
{
 int n;
 while(EOF != scanf("%d",&n))
 {
  if(n <= 0)
  {
   break;
  }
  int iArr[MAXSIZE];
  for(int i = 1 ; i <= n ; i++)
  {
   scanf("%d",&iArr[i]);
  }
  //pair<int,int> pairRes = elevatorAssign(iArr,n);
  pair<int,int> pairRes = elevatorAssign_optimized(iArr,n);
  printf("%d %d\n",pairRes.first,pairRes.second);
 }
}

int main(int argc,char* argv[])
{
 process();
 getchar();
 return 0;
}

 

抱歉!评论已关闭.