解题思路
搭建一座宽度为 n 的大厦,大厦可以看成由 n 块宽度为1的积木组成,第 i
块积木的最终高度需要是 h[i]
。
在搭建开始之前,没有任何积木(可以看成 n 块高度为 0 的积木)。接下来每次操作,可以选择一段连续区间 [L, R] ,然后将第 L 块到第 R 块之间(含第 L 块和第 R 块)所有积木的高度分别增加 1 。
求建造大厦的最少的操作次数。
递归:
每次都选择最大限度的连续区间 [left, right]
,其最小的高度为 mi
,其所在的下标记录在 vmin
。
这时,操作 mi
次,计入 cnt
,vmin
中的下标位置已经达到所需高度。
将区间从 [left, right]
以 vmin
中的下标为分界线,又分成若干个连续区间,直至区间不能分割为止。
C++代码
#include<cstdio> #include<vector> using namespace std; int opCnt(vector<int>& h, int left, int right){ if(left > right) return 0; if(left == right) return h[left]; vector<int> vmin; vmin.push_back(left); int mi = h[left]; for(int i=left+1; i<=right; ++i){ if(h[i] < mi){ vmin.clear(); vmin.push_back(i); mi = h[i]; } else if(h[i] == mi) vmin.push_back(i); } int cnt = mi; for(int i=left; i<=right; ++i) h[i] -= mi; cnt += opCnt(h, left, vmin[0]-1); for(int i=1; i<vmin.size(); ++i){ cnt += opCnt(h, vmin[i-1]+1, vmin[i]-1); } cnt += opCnt(h, vmin.back()+1, right); return cnt; } int main(){ int n; scanf("%d", &n); vector<int> h(n); for(int i=0; i<n; ++i) scanf("%d", &h[i]); int cnt = opCnt(h, 0, n-1); printf("%d", cnt); return 0; }
C++代码(贪心,差分)
#include<cstdio> #include<vector> using namespace std; int main(){ int n; scanf("%d", &n); vector<int> h(n); for(int i=0; i<n; ++i) scanf("%d", &h[i]); int cnt = h[0]; for(int i=1; i<n; ++i){ if(h[i] > h[i-1]) cnt += h[i]-h[i-1]; } printf("%d", cnt); return 0; }