1. 综教楼后的那个坑

题目

描述

  在 LIT 综教楼后有一个深坑,关于这个坑的来历,有很多种不同的说法。其中一种说法是,在很多年以前,这个坑就已经在那里了。这种说法也被大多数人认可,这是因为该坑有一种特别的结构,想要人工建造是有相当困难的。

  从横截面图来看,坑底成阶梯状,由从左至右的 1..N 个的平面构成(其中 1 ≤ N ≤ 100,000),如图:

   *            * :
   *            * :
   *            * 8
   *    **      * 7
   *    **      * 6
   *    **      * 5
   *    ********* 4 <- 高度
   *    ********* 3
   ************** 2
   ************** 1
平面 |  1  |2|   3    | 

每个平面 i 可以用两个数字来描述,即它的宽度 Wi 和高度 Hi,其中 1 ≤ Wi ≤ 1,000、1 ≤ Hi ≤ 1,000,000,而这个坑最特别的地方在于坑底每个平面的高度都是不同的。每到夏天,雨水会把坑填满,而在其它的季节,则需要通过人工灌水的方式把坑填满。灌水点设在坑底位置最低的那个平面,每分钟灌水量为一个单位(即高度和宽度均为 1)。随着水位的增长,水自然会向其它平面扩散,当水将某平面覆盖且水高达到一个单位时,就认为该平面被水覆盖了。

  请你计算每个平面被水覆盖的时间。

   灌水              水满后自动扩散
        |                       |                           
      * |          *      *     |      *      *            *
      * V          *      *     V      *      *            *
      *            *      *    ....    *      *~~~~~~~~~~~~*
      *    **      *      *~~~~** :    *      *~~~~**~~~~~~*
      *    **      *      *~~~~** :    *      *~~~~**~~~~~~*
      *    **      *      *~~~~**~~~~~~*      *~~~~**~~~~~~*
      *    *********      *~~~~*********      *~~~~*********
      *~~~~*********      *~~~~*********      *~~~~*********
      **************      **************      **************
      **************      **************      **************
   4 分钟后           26 分钟后        50 分钟后
   平面 1 被水覆盖     平面 3 被水覆盖    平面 2 被水覆盖输入

  输入的第一行是一个整数 N,表示平面的数量。从第二行开始的 N 行上分别有两个整数,分别表示平面的宽度和高度。

输出

  输出每个平面被水覆盖的时间。

C++代码(含注释)

#include
using namespace std;

struct Pit {
    long long width;
    int height;
    int id;
    Pit* prior;
    Pit* next;

    Pit() : width(0), height(0), id(0), prior(nullptr), next(nullptr) {}
    ~Pit() {}
};

long long ans[100010]; // 第id个平面需要时间ans[id]
void pourAndConnect(Pit* lowest) {
    long long time = 0; // 记录当前已经使用了的时间

    // 当前平面的前一个平面和后一个平面还有宽度,表示还需要倾倒水
    while (lowest->prior->width != 0 || lowest->next->width != 0) {
        ans[lowest->id] = time + lowest->width; // 记录当前平面的倾倒时间

        // 找到前一个和后一个平面中较低的那个
        Pit* low = lowest->prior->height < lowest->next->height ? lowest->prior : lowest->next;
        time += (low->height - lowest->height) * lowest->width; // 计算倾倒水过程需要的时间

        low->width += lowest->width; // 将当前平面和较低的平面合并

        lowest->prior->next = lowest->next; // 从链表中删除当前平面
        lowest->next->prior = lowest->prior;

        delete lowest;
        lowest = low; // 新的最低平面为较低的平面

        // 判断新的一轮倾倒水的平面
        if (lowest->height < lowest->prior->height && lowest->height < lowest->next->height) {
            continue;
        }
        else if (lowest->height > lowest->prior->height) {
            while (lowest->height > lowest->prior->height) {
                lowest = lowest->prior;
            }
        }
        else {
            while (lowest->height > lowest->next->height) {
                lowest = lowest->next;
            }
        }
    }

    ans[lowest->id] = time + lowest->width * 1; // 记录最后一个平面的倾倒时间
}

int main() {
    int N = 0;
    cin >> N;

    Pit* head = new Pit(); // 创建一个头结点
    Pit leftWall, rightWall;
    leftWall.width = leftWall.id = rightWall.width = rightWall.id = 0;
    leftWall.height = rightWall.height = 1000010;
    leftWall.prior = rightWall.next = NULL;

    Pit* current = head, * previous = &leftWall, * lowest = head;

    // 输入每个平面的宽度和高度
    for (int i = 0; i < N; i++) {
        cin >> current->width >> current->height;
        current->id = i + 1;

        if (current->height < lowest->height) {
            lowest = current; // 记录最低的平面
        }

        current->prior = previous; // 设置前一个平面
        if (i == N - 1) {
            current->next = &rightWall; // 最后一个平面的后一个平面为右墙
        }
        else {
            current->next = new Pit(); // 创建一个新的平面
        }

        previous = current;
        current = current->next;
    }

    pourAndConnect(lowest); // 进行倾倒和连接操作

    // 输出每个平面的倾倒时间
    for (int i = 0; i < N; i++) {
        cout << ans[i + 1] << endl;
    }

    return 0;
}

你可能感兴趣的:(数据结构与算法设计,c++)