首页 > 试题广场 >

选区间

[编程题]选区间
  • 热度指数:3908 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 128M,其他语言256M
  • 算法知识视频讲解

给定一个数组序列,需要求选出一个区间,使得该区间是所有区间中经过如下计算的值最大的一个:

区间中的最小数*区间所有数的和

最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列 [6 2 1]则根据上述公式,可得到所有可以选定各个区间的计算值:

[6] = 6 * 6 = 36;

[2] = 2 * 2 = 4;

[1] = 1 * 1 = 1;

[6,2] = 2 * 8 = 16;

[2,1] = 1 * 3 = 3;

[6, 2, 1] = 1 * 9 = 9;

从上述计算可见选定区间[6],计算值为36, 则程序输出为36。

区间内的所有数字都在[0, 100]的范围内;


输入描述:
第一行输入数组序列个数,第二行输入数组序列。


输出描述:
输出数组经过计算后的最大值。
示例1

输入

3
6 2 1

输出

36
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * n的范围[1,500000]太大,直接dp会超时
 *
 * 因为区间计算值都是由 区间最小值*区间和 得到的,
 * 所以在给定区间最小值的情况下,最优的情况一定是这个区间尽可能的扩展,
 * 直至区间的最小值不满足给定的最小值。
 * (扩展的过程因为满足 区间最小值 >= 给定区间最小值 的情况下,
 *      区间和一直增大,所以最终结果也增大)
 *
 * 又因为所有的数字都在[0, 100]的范围内,
 * 所以只需要遍历每一个数字为区间最小值的情况即可。
 *
 * 例如有序列[11, 12, 13, 1, 20, 21, 2, 30],设i表示给定的区间最小值
 * 当i=10时,即规定 区间最小值 >= 10,
 *     满足区间的条件有[11, 12, 13], [20, 21], [30]
 *     对应的计算值为 11*(11+12+13), 20*(20+21), 30*30
 *     在给定i=10的情况下,最优值为max(11*(11+12+13), 20*(20+21), 30*30)
 * 所以对i所有可能的情况都遍历一遍就能确定最终的最优值,i属于[0, 100]
 *
 * @author wylu
 */
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str;
        while ((str = br.readLine()) != null) {
            int n = Integer.parseInt(str);
            int[] a = new int[n];
            String[] numStrs = br.readLine().split(" ");
            for (int i = 0; i < n; i++) {
                a[i] = Integer.parseInt(numStrs[i]);
            }

            long res = 0;
            for (int i = 0; i <= 100; i++) {
                int realMin = Integer.MAX_VALUE; //真正的区间最小值
                long sum = 0;
                for (int j = 0; j < n; j++) {
                    if (a[j] >= i) {
                        sum += a[j];
                        realMin = Math.min(realMin, a[j]);
                    }else {
                        res = Math.max(res, realMin * sum);
                        sum = 0;
                        realMin = Integer.MAX_VALUE;
                    }
                }
                res = Math.max(res, realMin * sum);
            }
            System.out.println(res);
        }
    }
}
编辑于 2019-05-03 13:35:58 回复(5)
/*
单调栈:AC 100%,时间复杂度O(n)
栈:记录当前的最小值和对应这一区间累积和
对于第i个数,判断其与栈顶元素的大小关系:
  a. 如果比栈顶元素大,累积和=元素值,压栈
  b. 否则,栈中元素不断出栈,并计算栈元素*以此元素为最小值的最大累计和,直到栈空或满足a条件
对于得到的单调栈,再用b中的策略处理一次即可。
*/

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>

struct prs {
    int val;
    int acc;
};

using namespace std;
int main()
{
    int n;
    cin>>n;
    
    stack<prs> s1;
    prs ptmp;
    int maxval = 0, tmp = 0;
    
    for(int i = 0; i < n; i++) {
        cin>>ptmp.val;
        ptmp.acc = ptmp.val;
        if(i != 0 && ptmp.val <= s1.top().val) {
            tmp = 0;
            while(!s1.empty() && ptmp.val <= s1.top().val) {
                tmp = tmp + s1.top().acc;
                maxval = max(maxval, tmp*s1.top().val);
                s1.pop();
            }
            ptmp.acc = ptmp.acc + tmp;
        }
        s1.push(ptmp);
    }
    
    tmp = 0;
    while(!s1.empty()) {
        tmp = tmp + s1.top().acc;
        maxval = max(maxval, tmp*s1.top().val);
        s1.pop();
    }
    cout<<maxval<<endl;
    
    return 0;
}

发表于 2020-06-27 16:34:01 回复(0)
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int n,num[500001],sum=0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>num[i];
        sum+=num[i];
    }
    sort(num,num+n);
    cout<<sum*num[0]<<endl;
    return 0;
}

发表于 2019-06-25 22:16:47 回复(1)
单调栈,写得有些繁琐了,一开始左右区间更新的做法有bug,改了好久。
#include<iostream>
#include<stdio.h>
#include<math.h>
#include<cstring>
#include<string.h>
#include<vector>
#include<stack>
using namespace std;
int main()
{
    int n;
    cin>>n;
    vector<int> arr(n);
    for(int i=0;i<n;i++)
        cin>>arr[i];
    vector<long long> sum(n+1,0); //sum[i]表示arr[i]前面(不含arr[i])的所有数之和
    for(int i=1;i<=n;i++) {
        sum[i]+=sum[i-1]+arr[i-1];
    }
    vector<int> left(n,0); //left[i]:以arr[i]为最小值时区间的左边界(含该左边界)
    vector<int> right(n,0); //right[i]:以arr[i]为最小值时区间的右边界(含该右边界)
    stack<int> s;
    s.push(0);
    for(int i=1;i<n;i++) {
        if(arr[s.top()]<=arr[i]) {
            left[i]=i;
        }
        while(!s.empty() && arr[s.top()]>arr[i]) {
            int x=s.top(); s.pop();
            left[i]=left[x];
            right[x]=i-1;
        }
        s.push(i);
    }
    while(!s.empty()) {
        int x=s.top(); s.pop();
        right[x]=n-1;
    }
    long long maxnum=0;
    for(int i=0;i<n;i++) {
        maxnum=max(maxnum,arr[i]*(sum[right[i]+1]-sum[left[i]]));
    }
    cout<<maxnum<<endl;
    return 0;
}


编辑于 2021-05-11 16:07:57 回复(0)
方案一68.5%通过:选择固定的最小值,计算不同范围的累积值相乘得到的最大值
方案二(100%通过:基于单调栈O(n)式地计算最大值

方案一68.5%通过
N = int(input())
arr = list(map(int, input().split()))

ans = 0
left = min(arr)
right = max(arr) + 1
for i in range(left, right):
    minimum = i
    accumulation = 0
    for n in arr:
        if n >= minimum:
            accumulation += n
        else:
            accumulation = 0
        ans = max(ans, minimum*accumulation)
print(ans)

方案二(100%通过
N = int(input())
arr = list(map(int, input().split())) + [0]

ans = 0
id_stack = []
integral_arr = [0]
for i,n in enumerate(arr):
    while len(id_stack) and n < arr[id_stack[-1]]:
        idx = id_stack.pop()
        minimum = arr[idx]
        accumulation = integral_arr[i] if not id_stack else integral_arr[i] - integral_arr[id_stack[-1]+1]
        ans = max(ans, minimum * accumulation)
        
    id_stack.append(i)
    integral_arr.append(integral_arr[-1] + n)
    
print(ans)


编辑于 2020-12-27 18:05:53 回复(0)
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    scanf("%d", &n);
    int a[n+1], s[n+1];
    memset(a, 0, sizeof(a));
    memset(s, 0, sizeof(s));
    for(int i=1;i<=n;i++){
        scanf("%d", &a[i]);
        s[i] = s[i-1] + a[i];
    }
    int Max = a[1]*a[1], t, k;
    vector<int> S = {1};
    for(int i=2;i<=n;i++){
        t = S.back();
        while(a[t] >= a[i]){
            k = (S.size()>=2 ? S[S.size()-2] : 0);
            Max = max(Max, a[t]*(s[i-1]-s[k]));
            S.pop_back();
            if(S.empty())
                break;
            else
                t = S.back();
        }
        if(S.empty())
            Max = max(Max, a[i]*s[i]);
        else
            Max = max(Max, a[i]*(s[i]-s[t]));
        S.push_back(i);
    }
    while(!S.empty()){
        t = S.back();
        k = (S.size()>=2 ? S[S.size()-2] : 0);
        Max = max(Max, a[t]*(s[n]-s[k]));
        S.pop_back();
    }
    printf("%d\n", Max);
    return 0;
}

发表于 2020-12-17 00:31:12 回复(0)
单调栈:当前arr[k]>arr[i](i是当前栈顶元素),入栈否则出栈,计算以该位置元素为最小元素的序列中最大的乘积。

栈中元素记录数字序列的下标,栈顶两个元素i,j,和当前元素索引k的关系为:
j是序列的第j+1元素,到第k-1个元素中的最小元素所在位置。

#include<bits/stdc++.h>
using namespace std;
int res=0;
vector<int>arr;
vector<int>sum;
stack<int>mystack;
int main()
{
    int n;
    cin>>n;
    arr.resize(n+1,0);
    sum.resize(n+1,0);
    //为了统一操作,添加位置0;
    mystack.push(0);
    for(int i=1;i<=n;i++)
    {
        cin>>arr[i];
        sum[i]=sum[i-1]+arr[i];
        while(arr[i]<arr[mystack.top()])
        {
            int index=mystack.top();
             mystack.pop();
             res=max(res,arr[index]*(sum[i-1]-sum[mystack.top()]));
        }
        mystack.push(i);
     }
    while(mystack.size()>1)
    {
        int index=mystack.top();
        mystack.pop();
        res=max(res,arr[index]*(sum[n]-sum[mystack.top()]));
    }
    cout<<res<<endl;
    return 0;
}


发表于 2020-06-28 10:25:01 回复(0)
前缀和+单调栈
def main():
    n = int(input())
    num = list(map(int,input().split()))
    stack = [0]
    num = [0] + num +[0]
    res = 0
    cur_sum = [0]*(n+2)
    for i in range(1,n+2):
        cur_sum[i] = cur_sum[i-1] + num[i]
        while num[i] < num[stack[-1]]:
            cur_min = num[stack.pop()]
            c_sum = cur_sum[i-1] - cur_sum[stack[-1]]
            res = max(res,c_sum*cur_min)
        stack.append(i)
        
        
    return res
print(main())
            
    


发表于 2022-08-20 16:55:59 回复(0)
using System;
using System.Collections.Generic;

namespace CSExercise
{
    class Number
        {
            public int Value { get; set; } = -1;
            public int Left { get; set; } = 0;
            public int Right { get; set; } = 0;
        }
    
    class Program
    {
        static void Main(string[] args)
        {
            List<Number> nums = new List<Number>();
            Console.ReadLine();
            string[] str = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);
            List<int> sum = new List<int>();
            sum.Add(0);
            for (int i = 0; i < str.Length; i++)
            {
                int v = int.Parse(str[i]);
                sum.Add(sum[i] + v);

                Number n = new Number();
                n.Value = v;
                n.Left = 0;
                n.Right = str.Length - 1;
                nums.Add(n);
            }

            Stack<Number> UpStack = new Stack<Number>();

            // 从左往右来一遍
            for (int i = 0; i < nums.Count; i++)
            {
                // 如果栈为空,加入该数字
                if (UpStack.Count == 0)
                {
                    UpStack.Push(nums[i]);
                    continue;
                }

                // 如果该数字小于栈顶元素,则将栈顶元素踢出;直到不能踢出或栈为空
                while ((UpStack.Count > 0) && (nums[i].Value < UpStack.Peek().Value))
                {
                    UpStack.Pop().Right = i - 1;
                }

                UpStack.Push(nums[i]);
            }
            UpStack.Clear();

            // 从右往左来一遍,这里图省事,我没有写到函数里。就直接把代码复制粘贴了
            for (int i = nums.Count - 1; i >= 0 ; i--)
            {
                // 如果栈为空,加入该数字
                if (UpStack.Count == 0)
                {
                    UpStack.Push(nums[i]);
                    continue;
                }

                // 如果该数字小于栈顶元素,则将栈顶元素踢出;直到不能踢出或栈为空
                while ((UpStack.Count > 0) && (nums[i].Value < UpStack.Peek().Value))
                {
                    UpStack.Pop().Left = i + 1;
                }

                UpStack.Push(nums[i]);
            }

            int maxTotal = 0;

            foreach (Number item in nums)
            {
                maxTotal = Math.Max(maxTotal, item.Value * (sum[item.Right + 1] - sum[item.Left]));
            }

            Console.WriteLine(maxTotal);
        }
    }
}


发表于 2020-08-30 10:56:40 回复(0)
import java.util.*;

public class Main {
    /*
   public static void main(String[] args) {
        
       Scanner scanner = new Scanner(System.in);
       int n = scanner.nextInt();
       int i = 0;
       int[] input = new int[n];
       while (i < n) {
            int num = scanner.nextInt();
           input[i] = num;
           ++i;
       }
       int max = 0;
       for (int index = 0; index < n; ++index) {
           int sum = input[index];
           i = index - 1;
           while (i >= 0 && input[i] >= input[index]) {
               sum += input[i];
               --i;
           }
           i = index + 1;
           while (i < n && input[i] >= input[index]) {
               sum += input[i];
               ++i;
           }
           max = Math.max(max, sum * input[index]);
       }
        System.out.println(max);
    }
    */
    private static int proc(int[] input, int begin, int end) {
        if (end == begin) {
            return 0;
        }
        if (end - begin == 1) {
            return input[begin] * input[begin];          
        }
        int sum = 0;
        int min = Integer.MAX_VALUE;
        int index = -1;
        for (int i = begin; i < end; ++i) {
            if (input[i] < min) {
                index = i;
                min = input[i];
            }
            sum += input[i];
        }
        //分治时左右区间必须不含中间值,如(7,2,1,4,5)通过1划分为(7,2)和(1,4,5),(1,4,5)这个右侧区间将无法继续划分,陷入死循环
        return Math.max(sum * min, Math.max(proc(input, begin, index), proc(input, index + 1, end)));
    }
    
    public static void main(String[] args) {
        
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int i = 0;
        int[] input = new int[n];
        while (i < n) {
            int num = scanner.nextInt();
            input[i] = num;
            ++i;
        }
        System.out.println(proc(input, 0, n));
    }
}

单调栈我是没想明白,提供两种思路
思路1,注释中的代码,循环以每个元素为区间最小值,左右扩展试探,扩展到最大,滑动到下个中心
思路2,分治
编辑于 2020-08-22 12:42:15 回复(0)
  • 输入输出好不习惯啊

    public class Main {
      public static void main(String... args) {
          Scanner sc = new Scanner(System.in);
          int n = sc.nextInt();
          int[] arr = new int[n];
          for(int i=0; i<n; i++) {
              arr[i] = sc.nextInt();
          }
          sc.close();
    
          int res = 0;
          for(int i=0;i<n;i++) {
              int left = i-1;
              int right = i+1;
              int sum = arr[i];
              while(left>=0 && arr[left]>=arr[i]) {
                  sum+=arr[left];
                  left--;
              }
              while(right<n && arr[right]>=arr[i]) {
                  sum += arr[right];
                  right++;
              }
              res = Math.max(res, sum*arr[i]);
          }
          System.out.println(res);
      }
    }
发表于 2020-08-14 11:08:29 回复(1)
单调队列,每次弹出元素时计算以弹出元素作为最小值的区间结果。
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n, res=0;
    cin>>n;
    vector<int> a(n+10), sum(n+10, 0);
    stack<int> st;
    sum[0] = 0;
    for(int i=0;i<n;i++){
        cin>>a[i];
        sum[i+1] += sum[i]+a[i];
        while(!st.empty()&&a[i]<=a[st.top()]){
            int minima = a[st.top()];
            st.pop();
            int l =-1;
            if(!st.empty()) l = st.top();
            res = max(res, minima*(sum[i]- sum[l+1])); 
        }
        st.push(i);
    }
    
    while(!st.empty()){
        int minima = a[st.top()];
        st.pop();
        int l =-1;
        if(!st.empty()) l = st.top();
        res = max(res, minima*(sum[n]- sum[l+1]));
    }
    cout<<res;
}


发表于 2020-08-13 21:39:55 回复(0)
import java.io.IOException;
import java.util.Scanner;
public class Main {
	public static void main(String[] args) throws IOException{
		
		Scanner in=new Scanner(System.in);
		int N=in.nextInt();
		int arr[]=new int[500001];
		int max=0;
		
		for(int i=0;i<N;i++) {
			arr[i]=in.nextInt();
		}
		
		for(int i=0;i<N;i++) {
			//左
			int ans=0;
			for(int j=i;j>=0;j--) {
				if(arr[i]<=arr[j]) {
					ans+=arr[j];
				}else break;
			}
			
			//右
			for(int j=i+1;j<N;j++) {
				if(arr[i]<=arr[j]) {
					ans+=arr[j];
				}else break;
			}
			max=Math.max(max, (ans*arr[i]));
			
		}
		
		System.out.println(max);
		
			
			
		}
	
		
	
}

发表于 2020-06-18 17:03:16 回复(0)
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<set>
#define N 100005
using namespace std;
int main()
{
	int n;
	cin >> n;
	vector<int>v(n);
	set<int>s;
	for (int i = 0; i < n; i++)cin >> v[i], s.insert(v[i]);
	long long ans = 0;
	for (int c: s)//遍历数据中的所有不同数
	{
		int cur = 0, _max = -12456789;
		for (int i = 0; i < n; i++)//遍历数组,寻找以c为最小值的区间,_max记录区间内数字和的最大值
		{
			if (v[i] >= c)cur += v[i], _max = max(_max, cur);
			else cur = 0;
		}
		ans = ans > _max*c ? ans : _max*c;
	}
	cout << ans << endl;

}

发表于 2020-06-09 19:49:38 回复(0)
#python利用单调栈做 需要边缘处理所以在数组后多加一个0,且因为需要计算区间和所以又开了一个数组来记录防止超时 
n = int(input())
heights = list(map(int, input().split(" ")))
if n == 0:
    print(0)
heights.append(0)
#单调栈做  
stack, maxarea, pre_num = [], 0, [0]
for i in range(len(heights)):
    while stack and heights[i] < heights[stack[-1]]:
        right = stack.pop()               
        maxarea = max(maxarea, heights[right] * (pre_num[i] if not stack else (pre_num[i] - pre_num[stack[-1]+1])))
    pre_num.append(heights[i] + pre_num[-1])
    stack.append(i)
print(maxarea)
发表于 2019-07-02 20:38:53 回复(0)
参考楼上思想,c++实现版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
    int N;
    cin >> N;
    vector<int> vec(N);
    for (int i = 0; i < N; ++i)
        cin >> vec[i];
    int res = 0;
    for (int i = 1; i <= 100; ++i)
    {
        int nMin = 1000;
        int sum = 0;
        bool flag = true;
        for (int j = 0; j < N; ++j)
        {
            if (vec[j] >= i)
            {
                sum += vec[j];
                nMin = min(vec[j], nMin);
            }
            else
            {
                flag = false;
                res = max(res, sum*nMin);
                break;
            }
        }
        if (flag)
            res = max(res, sum*nMin);
    }
    cout << res << endl;
    return 0;
}

编辑于 2019-04-17 14:46:01 回复(0)

问题信息

上传者:小小
难度:
16条回答 4217浏览

热门推荐

通过挑战的用户

查看代码