找出将景观淹没到一定体积所需的水的高度
Find height of water needed to flood a landscape to a certain volume
我最近遇到了一个算法问题,目标是计算在一个建筑物宽度为1的城市中产生一定洪水所需的水位高度。
这与这里描述的二维雨水截留问题有点相似:
三维中截留雨水的最大体积
然而,在我的问题中,除了建筑物之间的积水外,我们还计算建筑物上方的积水。例如,以这个问题为例:
volume needed: 60
number of buildings: 3
heights of buildings: 30 40 20
这意味着我们必须计算所需的水位,这样一个建筑高度分别为30、40和20的城市就会出现至少60的洪水。
^
|
50 |~~~~~~~~~~~~~|
| | |
40 | ----- |
| | | | |
30 -----| | |
| | || | |
20 | || |-----
| | || || |
10 | || || |
| | || || |
.----------1----2----3---------->
在这种情况下,结果将是50,因为水位需要处于高度50,使得建筑物之间和建筑物上方的水量至少为60。在这里,每栋建筑上方的洪水分别为20、10和30,加起来正好是60。
我的尝试在时间和正确性方面都表现不佳:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int v;
int n;
cin >> v >> n;
vector<int> altitudes;
int count = 0;
int altitude;
while (count < n) {
cin >> altitude;
altitudes.push_back(altitude);
count++;
}
sort(altitudes.begin(), altitudes.end());
int vtemp = 0;
int i = 1;
int h = altitudes[0];
while (vtemp < v && i < n) {
h++;
if (h == altitudes[i]) {
i++;
}
vtemp = 0;
for (int j = 0; j < i; j++) {
vtemp += h - altitudes[j];
}
}
cout << h << endl;
return 0;
}
我在代码中为算法添加了注释。我在你的测试数据上测试了它,它有效。时间复杂度是O(n log n),因为我们需要对构建进行排序。在没有排序的情况下,算法在O(n)中工作
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int v;
int n;
cin >> v >> n;
vector<int> altitudes;
int count = 0;
int altitude;
while (count < n) {
cin >> altitude;
altitudes.push_back(altitude);
count++;
}
sort(altitudes.begin(), altitudes.end());
//-- changed from here
int current_level = altitudes[0]; //start from height of the smallest building
int length = 0;
while (v > 0)
{
++length; //go to next level
for (; length < altitudes.size() && altitudes[length] == altitudes[length - 1]; ++length); //find length of current water level
int height = v / length; //maximum possible height to fill
if (length < altitudes.size()) //if not all building in use
height = min(height, altitudes[length] - current_level); //fill with all water (height) or with the difference to next level
current_level += height; //increase level by height
v -= height * length; //decrease amount of water
if (length == altitudes.size() || v < length) //we filled the whole area, or there is no enough water to fill 1 'meter'
break;
}
cout << current_level << endl;
return 0;
}
将高度从最低到最高排序,然后开始填充最低的,直到其高度等于第二低,然后填充最低的和第二低的,直到它们的高度等于第三低,以此类推,直到水用完。
对建筑物进行排序时,您正处于正确的轨道上。这让我们对每栋建筑上方的水层有了更清晰的了解。
例如,假设我们有高度为4、2、3、1、2、5的建筑:
x
x x
x x x
xxx xx
xxxxxx
让我们给他们注水。该图使用1表示最底层的水层,使用2表示第二底层,依此类推:
44444x
x3333x
x2x22x
xxx1xx
xxxxxx
我们可以一排排地穿过建筑物,把水层加起来,直到达到所需的体积。然而,这需要O(nm)
时间,其中n
是建筑物的数量,m
是最大建筑物高度。我们可以做得更好。
让我们对建筑物进行分类。现在从左到右的高度是1、2、2、3、4、5:
x
xx
xxx
xxxxx
xxxxxx
再次,让我们淹没他们:
44444x
3333xx
222xxx
1xxxxx
xxxxxx
现在水层更容易计数了。每层的长度是具有相同高度的建筑物的数量加上前一层的长度。
这使我们能够在O(n)
时间内计算层数,因为我们扫描建筑高度一次,并对每栋建筑执行恒定数量的操作。对建筑物进行分类的成本为O(n log n)
。除非m
很小,否则O(n log n + n)
的总成本要好于O(nm)
。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int requiredVolume, n;
cin >> requiredVolume >> n;
vector<int> heights;
for (int i = 0; i < n; ++i) {
int height;
cin >> height;
heights.push_back(height);
}
// Sort the buildings by increasing height.
sort(heights.begin(), heights.end());
// Start with the required volume and subtract layers until we reach zero.
int volume = requiredVolume,
waterHeight = heights[0],
layerLength = 0,
pos = 0;
while (volume > 0) {
// Look for the next building that's taller than the current one.
int seek = pos;
while (seek < n && heights[seek] == heights[pos]) {
++seek;
}
// Extend the current water layer.
layerLength += seek - pos;
// Calculate the number of layers we would need to reach zero.
int needLayers = (volume + layerLength - 1) / layerLength;
// If we're at the tallest building, take all the layers we need.
// Otherwise, take layers up to the height of the next building.
int addLayers;
if (seek == n) {
addLayers = needLayers;
} else {
addLayers = min(heights[seek] - heights[pos], needLayers);
}
volume -= addLayers * layerLength;
waterHeight += addLayers;
cout << "with water at height " << waterHeight <<
", the volume is " << (requiredVolume - volume) << 'n';
// Advance to the next building.
pos = seek;
}
cout << "final answer:n minimum height = " << waterHeight <<
", volume reached = " << (requiredVolume - volume) << 'n';
return 0;
}
对于您在问题中给出的问题实例:
60 3
30 40 20
我们得到这个输出:
with water at height 30, the volume is 10
with water at height 40, the volume is 30
with water at height 50, the volume is 60
final answer:
minimum height = 50, volume reached = 60
如果我们想用上面的例子中的建筑实现至少11的体积:
11 5
4 2 3 1 2 5
我们得到这个:
with water at height 2, the volume is 1
with water at height 3, the volume is 4
with water at height 4, the volume is 8
with water at height 5, the volume is 13
final answer:
minimum height = 5, volume reached = 13
对答案进行二进制搜索以找到正确的高度。猜测一个高度并找到建筑物上方的体积,如果它超过期望的数量,则猜测更低,如果它低于期望的数量则猜测更高。继续此步骤,直到找到正确的高度。
- C++:如何根据地形高度更新玩家身高?
- 如何更改QComboBox项目的高度大小?
- 更改高度贴图,使其在 4x4 网格上显示 16 个 hieghtmap
- 我试图用这段代码找到二叉树的高度,但它一直返回 0,有人可以告诉我为什么吗?
- C++:如何计算二叉树中其值模块高度小于 2 的节点数?
- 如何使用 C++ gdal 库将栅格地理从 EGM96 转换为 WGS84 椭球体高度基准面
- 从具有父指针和左子树和右子树高度的树中删除
- 程序输入名称和高度并显示它。我不知道如何显示列表中最高人的姓名和身高
- 我正在尝试创建一个 C++ 贪吃蛇游戏,但我似乎无法绘制第二个高度边框,我该如何解决这个问题?
- 计算树高度的函数
- 尝试创建一个程序来查找非二叉树的高度.最终得到一个很长的循环,没有答案
- 计算BST返回-1的高度混淆
- 使用递归计算一个函数中的高度和大小
- 如何从2D数组为QHeightMapSurfaceDataProxy创建高度图以显示2D傅立叶变换结果
- 接受来自键盘的树节点以确定其高度
- 在 C++ 中使用平展数组语法,崩溃取决于宽度/高度
- 在一行中初始化指针(新uint8_t[高度*宽度*3])
- 在高度模板化的库中进行代码混淆
- ID3DXFont :文本被拉伸,如何获取字体的宽度和高度?
- 找出将景观淹没到一定体积所需的水的高度