「青训营 X 码上掘金」主题4的解答

146 阅读3分钟

当青训营遇上码上掘金

主题4的题目为:

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3] 
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
解答思路分享

刷过算法题目的朋友都应该比较熟悉,这道题类似于力扣上的接雨水问题(双指针) 这里我用自己的理解进行解答:

①首先前端代码:

只需要增加一个输入框,一个按钮,按钮响应后将custom端的结果返回到输出框中。

a. 需要在HTML代码中添加一个输入框元素,并为其添加一个id,

b. 添加一个按钮元素,并为其添加一个事件监听器,在监听器中读取输入框中的值,并将其传递给custom端

c. 添加一个输出框元素,并为其添加一个id。然后在JavaScript代码中读取custom端的输出,并将其写入输出框

代码较为简单,如下:

<div id="app"></div>
<div>
  <input type="text" id="input-array">
<button id="submit-btn">提交</button>

</div>
<div>
  <textarea id="output-result"></textarea>
</div>
<script type="module">
import {CodeXClient, logger} from 'https://cdn.jsdelivr.net/npm/jcode-tools@^0.10.2/dist/jcode-tools.esm.js';
(async () => {
  window.console = logger(app);
  console.log('题目: 现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)');
   console.log('请输入一个数组表示各个柱子高度(空格分隔),按确定后得出结果(耐心等待)');
  const inputArray = document.getElementById('input-array');
  const outputResult = document.getElementById('output-result');
  const submitBtn = document.getElementById('submit-btn');
  try {
    const code = new CodeXClient(); // 默认会从CustomCode中获取
    submitBtn.addEventListener('click', async () => {
     /*console.log('提交数组:', inputArray.value);-->*/
      const result = await code.runCode({input: inputArray.value});
      if(result?.error) {
        console.error(result.error);
      }
      /*console.log(result.output);*/
      outputResult.value = result.output;
    });
  } catch(ex) {
    console.error(ex);
  }
})();
</script>
②custom端

直接用c++进行解答即可。

思路: 最高的两个柱子(左边位置l,右边位置r)之间肯定可以装青豆,只需要遍历两根柱子区间就可以得出青豆数。依次找到在左边(或右边)第三高的柱子,这个柱子和原来的左边柱子(右边柱子)之间也可以装青豆,依此类推。

柱子高度记录在vector<int> A中,柱子的高度及其位置记录在vector<st> M

①将M根据柱子的高度进行排序。

最高的两个柱子(左边位置l,右边位置r)之间肯定可以装青豆,只需要遍历两根柱子区间就可以得出青豆数。

②从大到小按顺序遍历M: 初始化l=r=最高的柱子位置

a.如果柱子的位置t < l , 那么t到l之间可以装青豆数,遍历一下A数组的[t,l]区间即可

b.如果柱子的位置t > r , 那么t到l之间可以装青豆数,遍历一下A数组的[r,t]区间即可

c.其余情况不做处理

时间复杂度分析:O(nlog(n))

主要有三部分:排序 : O(nlogn) 遍历M : O(n) 遍历区间实际上只遍历了一遍A数组: O(n)

代码:

#!/jcode/lang/cpp https://xitu.github.io/jcode-languages/dist/lang-cpp.json

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int min(int a, int b)
{
	return a < b ? a : b;
}
struct st
{
	int height;
	int index;
	st() {};
	st(int h, int i) { height = h; index = i; }
	bool operator<(const st b)
	{
		return height < b.height;
	}
};
int main() {

	vector<int> A;
	vector<st> M;
	int t; int index = 0;
	while (cin >> t) {
		A.push_back(t);
		M.push_back(st(t, index));
		index++;
		if (index == 9) break;
	}
	if (A.size() == 1) cout << "可以装的青豆数量为:" << 0 << "(个单位)" << endl;
	int l, r;
	int res = 0;
	sort(M.begin(), M.end());
	for (int i=M.size()-1;i>=0;i--)
	{
		if (i==M.size()-1) {
			l = r = M[i].index; continue;
		}
		if(M[i].index>r) 
		{
			int t = M[i].index;
			int m = min(A[r], A[t]);
			for (int i = r + 1; i < t; i++) { res += m - A[i]; } 
			r = t;
		}
		if (M[i].index < l)
		{
			int t = M[i].index;
			int m = min(A[t], A[l]);
			for (int i = t+1; i < l; i++) { res += m - A[i]; }
			l = t;
		}
	};

	cout << "可以装的青豆数为:" << res << "(个单位)" << endl;
	return 0;
}