编程之美 - 爬楼梯问题

问题: 电梯在高峰时为了提高效率,当人们进入电梯选择好楼层后,根据算法只停在其中的一层。这个算法要求电梯里所有的人爬楼梯的总数最少。

想法 I:算是穷举法吧,把每个人可能会爬的楼层数都计算出来,然后逐一求和后,再找出和最小的值。

假设有10层楼,5个人分别到3,6,9,10,5 层,穷举一下可以得到

2  5  8  9  4   = 28         停在第 1   层爬楼的总数
1  4  7  8  3   = 23         停在第 2   层爬楼的总数
0  3  6  7  2   = 18         停在第 3   层爬楼的总数
1  2  5  6  1   = 15         停在第 4   层爬楼的总数
2  1  4  5  0   = 12         停在第 5   层爬楼的总数
3  0  3  4  1   = 11         停在第 6   层爬楼的总数
4  1  2  3  2   = 12         停在第 7   层爬楼的总数
5  2  1  2  3   = 13         停在第 8   层爬楼的总数
6  3  0  1  4   = 14         停在第 9   层爬楼的总数
7  4  1  0  5   = 17        停在第 10 层爬楼的总数

这里可以看出:到第6层停是可以满足要求的。

代码:测试 假设一共有5个人分别去 3,6,9,10和5层

#include <iostream>

using namespace std;

#define PEOPLE    5
#define FLOORS    10
#define MAX       50

int arrTarget[PEOPLE] = {3,6,9,10,5};
int arrResult[FLOORS][PEOPLE];
int arrClmib[FLOORS];

void print(int arr[FLOORS][PEOPLE])
{
    int i = 0, j = 0;
    for (i = 0; i < FLOORS; i++)
    {
        for(j = 0; j < PEOPLE;j++)
        {
            cout << arrResult[i][j] << "  ";
        }
        cout << " = " << arrClmib[i]<< endl;
    }
}

void scan()
{
    int i = 0, j = 0;
    int nMin = MAX, nMinIndex = 0; 
    memset(arrResult, -1, sizeof(int)*PEOPLE*FLOORS);

    for (i = 0; i < FLOORS; i++)
    {
        for(j = 0; j < PEOPLE;j++)
        {
            arrResult[i][j] = abs(i+1 - arrTarget[j]);
            arrClmib[i] += arrResult[i][j];
        }
        if (nMin > arrClmib[i])
        {
            nMin = arrClmib[i];
            nMinIndex = i;
        }
    }

    print(arrResult);
    cout << "\n========================================" << endl;
    cout << "stop at: floor " << nMinIndex+1 << "   clmib = " << nMin << endl;
}

void main()
{
    int i = 0;

    scan();

    cin >> i;
}

测试结果:
2  5  8  9  4   = 28
1  4  7  8  3   = 23
0  3  6  7  2   = 18
1  2  5  6  1   = 15
2  1  4  5  0   = 12
3  0  3  4  1   = 11
4  1  2  3  2   = 12
5  2  1  2  3   = 13
6  3  0  1  4   = 14
7  4  1  0  5   = 17

========================================
stop at: floor 6   clmib = 11

复杂度为 O(N*N)


想法 II: 利用你多爬一层我就少爬一层的关系,找一个最优的解。
假设就三层楼 i-1,  i,  i+1,到i-1层总共会爬N1层,到i层会爬N2,到 i+1会爬N3

编程之美 - 爬楼梯问题_第1张图片

如果电梯停在i-1层,那么也就有N1层的楼不用爬了。总数也就 Y -N1+N2+N3
如果电梯停在i+1层,那么也就有N3层的楼不用爬了。总数也就 Y+N1+N2 -N3

如果N1 > N2+N3那一定是停在i-1层合适,减去的楼层多了
如果N3 > N1+N2那一定是停在i+1层合适,减去的楼层多了

这样得到了算法II,先算出所有的要爬的楼层的总和,在按上面的计算方式每层向上计算。

代码:测试  假设一共有5个人分别去 3,6,9,10和5层
这里arrFloorPeople的数字表示到每一层的人数。

#include <iostream>

using namespace std;

#define FLOORS    10
int arrFloorPeople[FLOORS] = {0,0,1,0,1,1,0,0,1,1};

void Calc(int &nFloors, int& nFloorIndex)
{
    int i = 0;
    int N1 =0, N2 = 0, N3 = 0;
    int nMin = 0, nTargetFloor = 0;

    for (N1 = 0, N2 = arrFloorPeople[1], N3 = 0, i = 1; i < FLOORS; i++)
    {
        N3 += arrFloorPeople[i];
        nMin += arrFloorPeople[i] * (i-1);
    }

    for (i = 2; i < FLOORS; i++)
    {
        if(N1+N2 <N3)
        {
            nTargetFloor = i;
            nMin += (N1 + N2 - N3);
            N1 += N2;
            N2 = arrFloorPeople[i];
            N3 -= arrFloorPeople[i];
        }
        else
            break;
    }
    nFloors = nMin;
    nFloorIndex = nTargetFloor+1;
}

void main()
{
    int nFloors = 0; 
    int nFloorIndex = 0;

    Calc(nFloors, nFloorIndex);
    cout << "stop at: floor " << nFloorIndex << "   clmib = " << nFloors << endl;
    cin >> nFloors;
}

测试结果:

stop at: floor 6   clmib = 11

复杂度为 O(N)


你可能感兴趣的:(编程之美 - 爬楼梯问题)