首页 > 试题广场 >

计算题

[编程题]计算题
  • 热度指数:2967 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
给定n个非负整数表示每个宽度为1的柱子的高度题,计算按此排列的柱子,下雨之后能接多少雨水。

输入描述:
逗号分隔的整数,表示每根柱子的高度。
柱子数n<=1000000,每根柱子的高度不大于100000


输出描述:
雨水量(高度和)
示例1

输入

0,1,0,2,1,0,1,3,2,1,2,1

输出

6
""""
特殊的单调递增
序列a按照最高值划分为两个数组b、c,后半部分翻转
对每一个数组,遍历数组,
若当前最大值t_max小于x,则更新t_max = x;
若大于,则计数 t_max - x
"""


def count(a):
    ret, t_max = 0, 0
    for x in a:
        if x > t_max:
            t_max = x
        else:
            ret += t_max - x
    return ret


if __name__ == "__main__":
    a = list(map(int, input().strip().split(',')))
    b = a[a.index(max(a)):][::-1]
    c = a[:a.index(max(a)) + 1]
    ans = 0
    ans += count(b)
    ans += count(c)
    print(ans)
发表于 2019-07-14 18:44:02 回复(0)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;

public class Solution18_计算题_接雨水 {

    /**
     * LeetCode 42题 接雨水 难度 Hard
     * 此题提供四种解法供参考,由于牛客网的测试数据过大,结果会出现溢出,用 long 保存结果
     * 有两个方法没有AC过的。
     * 但是能提供不错的思路。
     */
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String[] strs = bf.readLine().split(",");
        int[] nums = new int[strs.length];
        for (int i = 0; i < strs.length; i++) {
            nums[i] = Integer.parseInt(strs[i]);
        }
        System.out.println(getWater3(nums));
    }

    /**
     * 方法一:暴力法 最容易联想到的办法 复杂度过高不能过
     * 计算每个位置可以接的水
     * height[i]  找出height[i]左边的最大值,和右边的最大值
     * 取其中的较小者减去height[i] 就是当前位置可以接到的水的量
     * 时间复杂度为 O(n^2)
     * 空间复杂度为 O(1)
     */
    private static long getWater1(int[] height) {
        if (height.length < 3) return 0;
        int n = height.length;
        long sum = 0;
        for (int i = 1; i < n - 1; i++) {//第一个位置和最最后一个位置肯定是不能接水的
            int left_max = 0, right_max = 0;//记录左边和右边的最大值
            for (int j = i - 1; j >= 0; j--) left_max = Math.max(left_max, height[j]);
            for (int j = i + 1; j < n; j++) right_max = Math.max(right_max, height[j]);
            int a = Math.min(left_max, right_max) - height[i];
            sum += a > 0 ? a : 0;
        }
        return sum;
    }

    /**
     * 方法二:牛客网能过
     * 暴力法每次都要去查找左边和右边的最大值
     * 我们不妨直接先把该值存储起来
     * 时间复杂度为 O(n)
     * 空间复杂度为 O(n)
     */
    public static long getWater(int[] height) {
        if (height.length < 2) return 0;
        int n = height.length;
        int[] left_max = new int[n];
        int[] right_max = new int[n];
        left_max[0] = height[0];
        right_max[n - 1] = height[n - 1];
        for (int i = 1; i < n; i++) {
            left_max[i] = Math.max(height[i], left_max[i - 1]);
            right_max[n - i - 1] = Math.max(height[n-i-1], right_max[n-i]);
        }
        long ans = 0;
        for (int i = 1; i < n - 1; i++) {
            ans += Math.min(left_max[i], right_max[i]) - height[i];
        }
        return ans;
    }

    /**
     * 方法三:借助栈 时间过久 没有通过
     * 如果不是很理解出栈过程,可以画画图便于理解。
     */
    private static long getWater3(int[] height) {
        long sum = 0;
        Stack<Integer> stack = new Stack<>();
        int p = 0;
        while (p < height.length) {
            //如果栈不为空并且当前指向的高度比栈顶的元素还大
            while (!stack.isEmpty() && height[p] > height[stack.peek()]) {
                //取出栈顶元素
                int h = height[stack.pop()];
                if (stack.isEmpty()) {
                    break;
                }
                int dc = p - stack.peek() - 1;//两墙之间的距离
                int min = Math.min(height[stack.peek()], height[p]) - height[h];
                sum += dc * min;
            }
            stack.add(p++);
        }
        return sum;
    }


    /**
     * 方法四:通过
     * 双指针 在一遍遍历中记录左边和右边的最大值
     */
    private static long getWater4(int[] height) {
        int max_left = 0, max_right = 0;
        long sum = 0;
        int point_left = 1, point_right = height.length - 2;
        for (int i = 1; i < height.length - 1; i++) {
            //从左往右更新
            if (height[point_left - 1] < height[point_right + 1]) {
                max_left = Math.max(max_left, height[point_left - 1]);
                int min = max_left - height[point_left];
                sum += min > 0 ? min : 0;
                point_left++;
            } else {
                max_right = Math.max(max_right, height[point_right + 1]);
                int min = max_right - height[point_right];
                sum += min > 0 ? min : 0;
                point_right--;
            }
        }
        return sum;
    }
}
发表于 2019-07-30 16:56:44 回复(0)
 
var str = readline();
var arr=str.split(',');
var num=trap(arr);
console.log(num)
    function trap (height) {
        let left = 0, right = height.length - 1
        let count = 0
        let leftMax = 0, rightMax = 0
        while (left <= right) {
            leftMax = Math.max(leftMax, height[left])
            rightMax = Math.max(rightMax, height[right])
            if (leftMax < rightMax) {
                count += leftMax - height[left]
                left++
            } else {
                count += rightMax - height[right]
                right--
            }
        }
        return count
        
    };

编辑于 2019-09-06 18:39:57 回复(0)
#include <bits/stdc++.h>
using namespace std;
int main()
{
    vector<int> a;
    string s;
    cin >> s;
    int cur=0, sign = 1;
    for (int i = 0; i < s.length(); i++)
    {
        if (!isdigit(s[i]))
        {
            a.push_back(cur * sign);
            cur = 0;
            sign = 1;
        }
        else
            cur = cur * 10 + s[i] - '0';
    }
    a.push_back(cur * sign);
    int n = a.size();
    // left[i]表示i左边的最大值,right[i]表示i右边的最大值
    vector<int> left(n), right(n);
    left[0]=0;
    right[n-1]=0;
    for (int i = 1; i < n; i++) 
        left[i] = max(left[i - 1], a[i - 1]);
    for (int i = n - 2; i >= 0; i--) 
        right[i] = max(right[i + 1], a[i + 1]);
    int water = 0;
    for (int i = 0; i < n; i++) 
    {
        int level = min(left[i], right[i]);
        water += max(0, level - a[i]);
    }
    cout<< water;
    return 0;
}
不知道什么原因,哪位大神指导一下
不通过
您的代码已保存 答案错误:您提交的程序没有通过所有的测试用例 case通过率为86.67%

编辑于 2019-07-04 16:00:31 回复(1)
#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    s+=',';
    istringstream ss(s);
    int temp,Max=INT_MIN,index;
    char c;
    vector<int>v;
    int i = 0;
    while(ss>>temp>>c)
    {
        v.push_back(temp);
        // 找到最高的柱子 以其为界 划分为左右两个容器 
        if(temp>Max)
        {
            Max =  temp;
            index = i;
        }
        i++;
    }
    int left_max = 0,right_max = 0;
    long sum = 0;
    int volume;
    if(v[0]>left_max) left_max = v[0]; 
    // 左边容器接水量
    for(int j = 1;j<index;j++)
    {
        // 当前位置接水量为 min(容器左边界,容器右边界)-当前位置的柱高
        volume = left_max-v[j];
        if(volume>0)
            sum+=volume;
        // 更新容器边界
        if(v[j]>left_max)
            left_max = v[j];
    }
    if(v[v.size()-1]>right_max) right_max = v[v.size()-1];
    // 右边容器接水量
    for(int k=v.size()-2;k>index;k--)
    {
        volume = right_max-v[k];
        if(volume>0)
            sum+=volume;
        if(v[k]>right_max)
            right_max = v[k];
    }
    cout<<sum<<endl;
    return 0;
}

编辑于 2019-08-15 10:41:35 回复(0)
亲测可用:
defcount(a):
   ret, t_max=0,0
   forxina:
       ifx > t_max:
           t_max=x
       else:
           ret+=t_max-x
   returnret
 
if__name__=="__main__":
   a=list(map(int,input().strip().split(',')))
   b=a[a.index(max(a)):][::-1]
   c=a[:a.index(max(a))+1]
   ans=0
   ans+=count(b)
   ans+=count(c)
   print(ans)

发表于 2022-07-02 09:27:28 回复(0)
#include <bits/stdc++.h>

using namespace std;
//构造单调栈,参考的是左神的最大子矩阵那题
long long fun(const vector<int> nums){
    stack<int> st;
    long long sum = 0;
    int len = nums.size();
    for(int i = 0; i < len; i++){
        while(!st.empty() && nums[i] > nums[st.top()]){
            int water_pos = st.top();st.pop();
            if(!st.empty()){
                int left_pos = st.top();
                long long height = min(nums[i],nums[left_pos]) - nums[water_pos];
                long long width = i - left_pos -1;
                sum += height * width;
            }
            else break;
        }
        st.push(i);
    }
    return sum;
}



int main(){
    vector<int> nums;
    string temp_str;
    int temp_num;
    while(getline(cin,temp_str,',')){
        temp_num = atoi(temp_str.c_str());
        nums.push_back(temp_num);
    }
    cout << fun(nums) << endl;
}
发表于 2022-03-02 20:11:52 回复(0)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define forward(from,to) for(int i=from;i<to;++i)
#define back(from,to) for(int i=from;i>to;--i)

int d[1000001];
int main() {
	int count = 0;
	int maxValue = 0;
	int maxIndex = 0;
	while (scanf("%d", &d[count])) {
		if (d[count] > maxValue) {
			maxValue = d[count];
			maxIndex = count;
		}
		++count;
		if (getchar() == '\n')break;
	}

	long long answer = 0;
	int cvalue = 0;
	forward(0, maxIndex) {
		if (cvalue < d[i])cvalue = d[i];
		else answer += cvalue - d[i];
	}

	cvalue = 0;
	back(count - 1, maxIndex) {
		if (cvalue < d[i])cvalue = d[i];
		else answer += cvalue - d[i];
	}

	printf("%lld\n", answer);
	return 0;
}

发表于 2021-09-11 02:12:43 回复(0)
import java.util.Scanner;

public class Main {

    // 思路:先将字符串分割,从前往后,选择第一个板作为参照,直到找到第二块比它高或者和它一样高的板在计算盛水量,
    // 记得计算损失量,将第二块板作为下一次的起始板,继续遍历;然后从后往前进行相同的遍历。
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        String line = scanner.nextLine();

        String[] split = line.split(",");

        int[] arr = new int[split.length];

        for (int i = 0; i < split.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
        }

        System.out.println(receiveRainwater(arr));
    }

    public static long receiveRainwater(int[] arr) {

        if (arr.length == 0) {
            return 0;
        }

        long amount = 0;
        int start = 0;
        while (true) {
            int index = start + 1;
            if (index == arr.length) {
                break;
            }
            long deBuff = 0;
            while (index < arr.length) {
                if (arr[start] <= arr[index]) {
                    break;
                }
                deBuff += arr[index];
                index++;
            }
            if (index == arr.length) {
                break;
            }
            amount += (long) (index - start - 1) * (arr[start] > arr[index] ? arr[index] : arr[start]) - deBuff;
            start = index;
        }

        int end = start - 1;
        start = arr.length - 1;
        while (true) {
            int index = start - 1;
            if (index == end) {
                break;
            }
            long deBuff = 0;
            while (index > end) {
                if (arr[start] <= arr[index]) {
                    break;
                }
                deBuff += arr[index];
                index--;
            }
            if (index == end) {
                break;
            }
            amount += (long) (start - index - 1) * (arr[start] > arr[index] ? arr[index] : arr[start]) - deBuff;
            start = index;
        }

        return amount;
    }
}
改进:
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

public class Main {

    // 思路:先将字符串分割,从前往后,选择第一个板作为参照,直到找到第二块比它高或者和它一样高的板在计算盛水量,
    // 记得计算损失量,将第二块板作为下一次的起始板,继续遍历;然后从后往前进行相同的遍历。
    public static void main(String[] args) {

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); // bufferedraader+inputstream 更快

        String line = null;
        try {
            line = bufferedReader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        String[] split = line.split(","); 

        int[] arr = new int[split.length];

        for (int i = 0; i < split.length; i++) {
            arr[i] = Integer.parseInt(split[i]);
        }

        System.out.println(receiveRainwater(arr));
    }

    public static long receiveRainwater(int[] arr) {

        if (arr.length == 0) {
            return 0;
        }

        long amount = 0;
        int start = 0;
        while (true) {
            int index = start + 1;
            if (index == arr.length) {
                break;
            }
            long deBuff = 0;
            while (index < arr.length) {
                if (arr[start] <= arr[index]) {
                    break;
                }
                deBuff += arr[index];
                index++;
            }
            if (index == arr.length) {
                break;
            }
            amount += (long) (index - start - 1) * (arr[start] > arr[index] ? arr[index] : arr[start]) - deBuff;
            start = index;
        }

        int end = start - 1;
        start = arr.length - 1;
        while (true) {
            int index = start - 1;
            if (index == end) {
                break;
            }
            long deBuff = 0;
            while (index > end) {
                if (arr[start] <= arr[index]) {
                    break;
                }
                deBuff += arr[index];
                index--;
            }
            if (index == end) {
                break;
            }
            amount += (long) (start - index - 1) * (arr[start] > arr[index] ? arr[index] : arr[start]) - deBuff;
            start = index;
        }

        return amount;
    }
}


编辑于 2021-05-06 21:13:48 回复(0)
装满水后的整体形状必然是单峰型。所以先从左边遍历,找到最高柱子的位置,然后从左端开始遍历到该位置,凡遇到右边相邻柱子低于左边的,将其填充为左边柱子高度,累加填充值。再用同样的方法从右边遍历,继续累加填充值。最后再将左右峰值位置之间的所有柱子填充为峰值高度,继续累加填充值。输出填充值即可。
def main():  
    H = list(map(int, input().split(',')))
    n = len(H)
    if n < 3: return 0
    Q = 0
    peak = max(H)
    p = [0,0]
    for j in range(2):
        H.reverse()
        p[j] = H.index(peak)
        for i in range(1,p[j]):
            if H[i] < H[i-1]:
                Q += H[i-1] - H[i]
                H [i] = H[i-1]
    if p[0] + p[1] != n - 1:
        Q += (n-p[0]-p[1]-2) * peak - sum(H[p[1]+1: -p[0]-1])
    return Q
   
if __name__ == '__main__':
    print(main())


发表于 2020-05-22 19:26:58 回复(0)
双指针法
l=[int(i) for i in input().split(',')]
i=0 ;j=len(l)-1
lh=l[0] ;rh=l[-1]
s=0
while i<j:
    if l[i]<=l[j]:
        i+=1
        if l[i]<=lh:s+=lh-l[i]
        else:lh=l[i]
    else:
        j-=1
        if l[j]<=rh:s+=rh-l[j]
        else:rh=l[j]
print(s)


发表于 2020-04-30 13:11:01 回复(0)
function cal_rain(arr) {
  if (arr.length === 0) {
    return 0;
  }

  let left = arr[0];
  let tmpArr = [];
  let rain = 0;
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] >= left) {
      //开始计算雨水量
      tmpArr.forEach(item => {
        rain += left - item;
      });
      left = arr[i];
      tmpArr = [];
    } else {
      tmpArr.push(arr[i]);
    }
  }

  //剩余部分
  let high = tmpArr[0];
  let highIndex = 0;
  tmpArr.forEach((item, index) => {
    if (item >= high) {
      high = item;
      highIndex = index;
    }
  });
  for (let i = 0; i < highIndex; i++) {
    rain += high - tmpArr[i];
  }
  return rain;
}

想法:先记录一个高度值,遍历数组,如果高度小于之前的高度,则存入一个数组;如果大于之前的高度,那么这之间就可以承接雨水。遍历完后,最后剩余的高度值需要另外处理。

复杂度为O(n),不知道有没有问题
发表于 2020-03-01 21:45:48 回复(0)
#include <bits/stdc++.h>
(755)#define UF(i,start,end) for(int i=start;i<end;i++)
#define DF(i,start,end) for(int i=start;i>=end;i--)


//freopen("temp.in","r",stdin);
using namespace std;

/*
    找先降后升的序列
    //两个栈,一个从左到右,一个从右到左都维护递增序列,会在最高处碰头
    //如果两个递增的点的位置相差超过1,则可以计算其接水度

*/
//全部相加可能溢出
int main(){
    string s;
    cin>>s;
    vector<int> V;
    int start=0,index;
    while((index=s.find(",",start))!=string::npos){
        V.push_back(atoi(s.substr(start,index-start+1).c_str()));
        start = index+1;
    }
    V.push_back(atoi(s.substr(start).c_str()));
    
    stack<int> S,T;
    int n = V.size();
    for(int i=0;i<n;i++){
        if(S.empty()) S.push(i);
        else if(V[i]>=V[S.top()]){
            S.push(i);
        }
    }
    int m = 0;//从右往左找时的边界 左边已经找到这了
    if(!S.empty()) m = S.top();
    for(int i=n-1;i>=m;i--){
        if(T.empty()) T.push(i);
        else if(V[i]>=V[T.top()]){
            T.push(i);
        }
    }
    long long ans = 0;
    while(T.size()>=2){
        int l = T.top();
        T.pop();
        if(l==T.top()-1) continue;
        for(int i=T.top()-1;i>l;i--){
            ans+=V[T.top()]-V[i];
        }
    }
    while(S.size()>=2){
        int r = S.top();
        S.pop();
        if(r==S.top()+1) continue;
        for(int i=S.top()+1;i<r;i++){
            ans+=V[S.top()]-V[i];
        }
    }
    cout<<ans<<endl;
    
    
    return 0;
}

发表于 2020-02-21 08:37:02 回复(0)
思路:前后两次遍历
从左往右遍历:假设最右边有一个无穷高的柱子,此时在每一个柱子上的储水量是多少
从右往左遍历:假设最左边有一个无穷高的柱子,此时在每一个柱子上的储水量是多少。这个量和上面的量求最小值
heights=list(map(int,input().strip().split(',')))
size=len(heights)
waters=[0]*size
val=heights[0]
for i in range(1,size):
    if val>heights[i]:
        waters[i]=val-heights[i]
    else:
        val=heights[i]
waters[-1]=0
val=heights[-1]
for i in range(size-2,-1,-1):
    if val>heights[i]:
        waters[i]=min(waters[i],val-heights[i])
    else:
        val=heights[i]
        waters[i]=0
print(sum(waters))


发表于 2020-02-08 16:13:33 回复(0)
hhh,超时。
arrs = list(map(int, input().split(',')))
max_left,max_right = [],[]
temp_left,temp_right=0,0
for i in range(len(arrs)):
    max_left.append(temp_left)
    if arrs[i]>temp_left:
        temp_left = arrs[i]
for i in range(len(arrs)-1,-1,-1):
    max_right.append(temp_right)
    if arrs[i]>temp_right:
        temp_right = arrs[i]
res = 0
for i in range(len(arrs)):
    curr = min(max_right[len(arrs)-i-1],max_left[i])-arrs[i]
    if curr>0:
        res+=curr
print(res)

发表于 2019-07-12 10:08:49 回复(0)
import java.util.Scanner;

public class Main{
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        Scanner sc = new Scanner(System.in);
        String[] a = sc.nextLine().split(",");
        int[] m = new int[a.length];
        for(int i=0; i<a.length; i++) {
            m[i] = Integer.parseInt(a[i]);
        }
        int s = getSpace(m);
        System.out.println(s);
    }

    private static int getSpace(int[] m) {
        //遍历最高的柱子,获取最大高度和下标
        int max = 0;
        int max_index = 0;
        for(int i=0; i<m.length; i++) {
            if(m[i] > max) {
                max = m[i];
                max_index = i;
            }
        }
        // 从最左端遍历到max_index,计算左边的雨水面积(高度下降时计算水位)
        int sum = 0;
        int cur = 0;
        int l, r;
        for(l=0; l<max_index; l++) {
            if(cur < m[l])
                cur = m[l];
            else
                sum += (cur - m[l]);
        }
        // 从最右端遍历到max_index,计算右边的雨水面积(高度下降时计算水位)
        for(r=m.length-1,cur=m[m.length-1]; r>max_index; r--) {
            if(cur < m[r])
                cur = m[r];
            else
                sum += (cur - m[r]);
        }
        return sum;
    }

}

关键

找到最高的柱子,并确实其位置
发表于 2019-06-13 22:40:40 回复(3)