首页 > 试题广场 >

回转寿司

[编程题]回转寿司
  • 热度指数:8524 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

小美请小团吃回转寿司。转盘上有N盘寿司围成一圈,第1盘与第2盘相邻,第2盘与第3盘相邻,…,第N-1盘与第N盘相邻,第N盘与第1盘相邻。小团认为第i盘寿司的美味值为A[i](可能是负值,如果小团讨厌这盘寿司)。现在,小团要在转盘上选出连续的若干盘寿司,使得这些寿司的美味值之和最大(允许不选任何寿司,此时美味值总和为0)。


输入描述:

第一行输入一个整数T(1<=T<=10),表示数据组数。

每组数据占两行,第一行输入一个整数N(1<=N<=10^5);

第二行输入N个由空格隔开的整数,表示A[1]到A[N](-10^4<=A[i]<=10^4)。



输出描述:

每组数据输出占一行,输出一个整数,表示连续若干盘寿司的美味值之和的最大值。

示例1

输入

1
4
3 -2 4 -1

输出

6

说明

美味值之和最大连续若干盘寿司为第3盘、第4盘和第1盘。 
题目要求环形数组的连续子数组的最大和,我们先不要去管数组是环形的情况,利用动态规划求解连续子数组的最大和以及最小和。
(1) 不考虑环形得到的最大值:题中允许寿司首尾相连的环形数组情况,因此常规求得的连续子数组的最大和就是我们排除这种情况之外的所有情况中的最大和。
(2) 只考虑环形得到的最大值:而对于首尾相连的情况,我们可以这样考虑,如果常规求得的连续子数组的和达到了最小,那么总和减去这个最小值就等于首尾相连情况的最大值了。
因此最大的美味值就是(1)和(2)两种情况中大的那个。
---------------------------------------------------------------------------------------------------------------------------
接下来说一下动态规划如何求解连续子数组的最大和:
状态定义:dp[i]表示以 结尾的连续子数组的最大和
状态转移方程:dp[i] = max(array[i], dp[i-1]+array[i])
状态转移方程的意思是:如果选择了当前元素i,而dp[i-1]为负数,表明之前的和做的是负贡献,会使得整体的和变小,因此这时候选择从array[i]重新开始计算和。
考虑到我们并不需要求得dp数组中所有的值,而是只需要最大值,所以还可以对空间复杂度进行优化。每次计算得到其中一个dp[i]时,就更新当前的最大值,而dp[i]之前的取值(dp[0],dp[1],...,dp[i-1])已经用过,所以不需要再保留了,仅用一个变量代替dp数组即可。
求解连续子数组的最小和只要将以上的max改成min就可以了......
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine().trim());
        while(T-- > 0){
            int n = Integer.parseInt(br.readLine().trim());
            String[] strArr = br.readLine().trim().split(" ");
            int[] yummy = new int[n];
            int sum = 0;
            for(int i = 0; i < n; i++){
                yummy[i] = Integer.parseInt(strArr[i]);
                sum += yummy[i];
            }
            // 为了降低时间复杂度,可以两种情况一起求
            int max = yummy[0];
            int min = yummy[0];
            int dpMax = yummy[0];
            int dpMin = yummy[0];
            for(int i = 1; i < n; i++){
                dpMax = Math.max(dpMax + yummy[i], yummy[i]);
                max = Math.max(max, dpMax);
                dpMin = Math.min(dpMin + yummy[i], yummy[i]);
                min = Math.min(min, dpMin);
            }
            System.out.println(Math.max(sum - min, max));
        }
    }
}


编辑于 2021-03-07 10:47:02 回复(20)
#include <bits/stdc++.h>
using namespace std;
int main(){
    int T,N;
    cin>>T;
    for(int i=0;i<T;i++){
        cin>>N;
        int Max=INT_MIN,Min=INT_MAX,res;
        vector<int> arr(N);
        for(int i=0;i<N;i++){
            cin>>arr[i];
        }
        int total=0;
        vector<int>dpMax(N);
        vector<int>dpMin(N);
        dpMax[0]=dpMin[0]=arr[0];
        total+=arr[0];
        //分析,最大值为单个序列最大或是首尾相连(总和减去最小值),取两者最大
        for(int i=1;i<N;i++){
            total+=arr[i];
            dpMax[i]=max(dpMax[i-1]+arr[i],arr[i]);
            dpMin[i]=min(dpMin[i-1]+arr[i],arr[i]);
            if(Max<dpMax[i]) Max=dpMax[i];
            if(Min>dpMin[i]) Min=dpMin[i];
        }
        res = max(Max,total-Min);
        cout<<res<<endl;
    }
        return 0;
}


注意以下几点;
* dp找到每个的最大值,常规(可以不dp,两个变量也可)
* 首尾相连的情况,逆向思维,所有的值减最小值就是首尾相连的最大值
* 两者取大
res = max(Max,total-Min);
即可
发表于 2021-03-01 20:37:33 回复(2)
def findMaxSum(N,score):
    '''
    动态规划的想法:
    pre[i]储存以第i个数结尾的连续数组的最大和,这样的连续数组只有两种可能
    第一种,这个数组只有一个数,即第i个数
    第二种,这个数组多于一个数,那么就是第i个数加以第i-1个数结尾的连续数组的最大和pre[i-1]
    取这两个值的最大值
    '''
    pre = [0]
    for i in range(N):
        maxSum = max(score[i],score[i]+pre[i])
        pre.append(maxSum)
    return max(pre)


T = int(input())
while T:
    T = T-1
    N = int(input())
    score = list(map(int, input().split()))
    maximum = findMaxSum(N,score)
    minimum = findMaxSum(N,[-i for i in score])
    if sum(score)+minimum>maximum:
        maximum = sum(score)+minimum
    print(maximum)

发表于 2021-03-03 21:38:32 回复(0)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine().trim());
        while (T-- > 0) {
            int n = Integer.parseInt(br.readLine().trim());
            String[] strArr = br.readLine().trim().split(" ");
            int[] yummy = new int[n];
            int sum = 0;
            for (int i = 0; i < n; i++) {
                yummy[i] = Integer.parseInt(strArr[i]);
                sum += yummy[i];
            }
            int m1=0,m2=0;
            int res1=Integer.MIN_VALUE;
            int res2=Integer.MAX_VALUE;
            // res1  无环情况下的最大值
            for(int i=0;i<yummy.length;i++){
                m1+=yummy[i];
                res1=Math.max(m1,res1);
                if(m1<0){
                    m1=0;
                }
            }
            //res2   无环情况下的最小值  数组的总和减去无环情况下的最小值就是有环情况下的最大值
            for(int i=0;i<yummy.length;i++){
                m2+=yummy[i];
                res2=Math.min(m2,res2);
                if(m2>0){
                    m2=0;
                }
            }
            System.out.println(Math.max(res1,sum-res2));
        }
    }
}
发表于 2022-07-03 16:51:04 回复(0)
//跟着楼上大神思路学的
import java.util.*;
public class Main {
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        String s = input.nextLine().trim();
        int m = Integer.parseInt(s);//共有m组数据
        for(int i = 0;i < m;i++){

            int n = Integer.parseInt(input.nextLine().trim());//有n个寿司

            int[] A = new int[n];//存放寿司美味值
            int totalSum = 0;//寿司美味值总和

            String[] s1 = input.nextLine().split(" ");
            for(int j = 0;j < n;j++){
                A[j] = Integer.parseInt(s1[j]);//接收美味值数据
                totalSum += A[j];//计算美味值总和
            }

            int resSum = Integer.MIN_VALUE;//存放最终结果-环形子数组最大和
            int maxSum = Integer.MIN_VALUE;//存放无环情况下,A[]数组的最大累加和
            int minSum = Integer.MAX_VALUE;//存放无环情况下,A[]数组的最小累加和
            int[] dpMaxSum = new int[n];//当遇到下一个A[i]为正的时候,贡献度为正,那么就继续累加,贡献度为负,那么就从A[i]重现计算贡献度
            int[] dpMinSum = new int[n];//
            dpMaxSum[0] = A[0];
            dpMinSum[0] = A[0];

            for(int j = 1;j < n;j++){
                dpMaxSum[j] = Math.max(dpMaxSum[j - 1] + A[j],A[j]);
                dpMinSum[j] = Math.min(dpMinSum[j - 1] + A[j],A[j]);
                maxSum = Math.max(dpMaxSum[j],maxSum);
                minSum = Math.min(dpMinSum[j],minSum);
            }
            /*这里这样理解->分两种情况:
           情况一:A数组首尾元素没有同时在最大累加和的子数组中,对应这里的maxSum
            情况二:A数组收尾元素同时在最大累加和的子数组中,因而最小累加和子数组
                便对应的是无环的情况,所以用A数组元素总和减去无环最小累加和即为所得

             */
            System.out.println(Math.max(totalSum - minSum,maxSum));

        }
    }
}


发表于 2021-08-08 18:37:57 回复(0)
import sys

T = int(sys.stdin.readline().strip('\n'))
for i in range(T):
    N = int(sys.stdin.readline().strip('\n'))
    l = [int(x) for x in sys.stdin.readline().strip('\n').split()]
    r_min = l.copy()
    r_max = l.copy()
    for i in range(1, len(l)):
        r_min[i] = min(r_min[i],r_min[i-1]+r_min[i]) #找到非环形最小和
        r_max[i] = max(r_max[i], r_max[i - 1] + r_max[i]) #非环形最大和
    print(max(sum(l)-min(r_min),max(r_max))) # max(total-非环形最小和,非环形最大和)

发表于 2021-07-17 16:45:13 回复(0)
貌似题解都是dp啊qwq提供一个单调队列解法。
先化环为链。复制第1~n-1个元素到数组之后。接着我们考虑前缀和数组,则问题转化为求ps[i]-min(ps[j]),i-n <= j <= i-1。这就得到一个滑动窗口求最小值的模板题啦~
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 1e5 + 5;

int n,a[N<<1],ps[N<<1];

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}

int main(int argc, char** argv) {
    int T;read(T);
    while(T--){
        read(n);
        rep(i,1,n) read(a[i]);
        re_(i,1,n) a[n+i] = a[i];
        n = 2*n-1;
        rep(i,1,n) ps[i] = ps[i-1]+a[i];
        deque<int> q;
        q.push_back(0);
        int ans = 0;
        rep(i,2,n){
            while(!q.empty() && q.front() < i-(n+1)/2) q.pop_front();
            ans = max(ans,ps[i]-ps[q.front()]);
            while(!q.empty() && ps[q.back()] > ps[i]) q.pop_back();
            q.push_back(i);
        }
        printf("%d\n",ans);
    }
    return 0;
}


发表于 2021-05-07 12:37:00 回复(0)
动态规划问题:
        先说结论:max(非环的最大DP值,总和 - 非环的最小DP值)
        首先,说成环带来的问题:成环后,可以有中间某段元素不选,而选到首尾元素的情况。
        非环的最大DP值:当最大值不涉及成环,则使用普通的DP可以找到最大连续数组的值。
        总和 - 非环的最小DP值:当最大值涉及成环,则可以通过DP找到最小连续数组的值,这个值是由一段数组组成,而且是中间的某段,那么除了这一段,剩下的可以通过环,连接成另一段连续数组,且数值是最大的,故使用 总和 - 非环的最小DP值 计算得到。
#include <bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int n;
        cin >> n;
        vector<int> nums(n, 0);
        for (int i = 0; i < n; ++i)
            cin >> nums[i];
        int dp[n][2];
        dp[0][0] = nums[0] >= 0? nums[0] : 0;
        dp[0][1] = nums[0] <= 0? nums[0] : 0;
        int max_value = dp[0][0], min_value = dp[0][1], sum = nums[0];
        for (int i = 1; i < n; ++i)
        {   
            sum += nums[i];
            dp[i][0] = max(dp[i - 1][0] + nums[i], nums[i]);
            max_value = max(max_value, dp[i][0]);
            dp[i][1] = min(dp[i - 1][1] + nums[i], nums[i]);
            min_value = min(min_value, dp[i][1]);
        }
        cout << max(max_value, sum - min_value) << endl;
    }
}

发表于 2021-03-25 22:35:36 回复(1)
#include<bits/stdc++.h>
using namespace std;

int T,N;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>T){
        while(cin >> N){
            vector<int> sushi(N);
            for(int i = 0;i < N;i++){
                cin>>sushi[i];
            }
            int res = INT_MIN;
            //分别统计0-N和超过N的情况,超过N的情况就是求内部最小值,用数组和减去它即可
            int nio = 0, min_nio = INT_MAX,max_nio = INT_MIN, min_inter = INT_MAX;
            for(int i = 0;i < N;i++){
                nio += sushi[i%N];
                min_nio = min(min_nio, nio);
                max_nio = max(max_nio, nio);
                min_inter = min(min_inter, nio - max_nio);
                res = max(res, nio - min_nio);
            }
            //由边界向内部    
            
            printf("%d\n",max(res, nio- min_inter));
        }
    }
}

编辑于 2022-01-22 14:14:17 回复(0)
有大佬帮忙看一下滑动窗口解法吗,用的是滑动窗口+双倍数组,只过了3/10,没找到BUG。
import java.util.Scanner;

// 解法:滑动窗口
// (1)最大连续子数组问题
// (2)首位相连问题
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        
        int nGroup = in.nextInt();
        while (in.hasNextInt()) {
            // 1.输入处理
            //   2n数组解决循环问题
            int n = in.nextInt();
            int[] arr = new int[2*n];   
            for (int i=0; i<n; i++) {
                arr[i] = in.nextInt();
            }
            for(int i=n; i<2*n; i++){
                arr[i] = arr[i-n];
            }

            // 2.滑动窗口
            int sum=0, max = Integer.MIN_VALUE;
            int l=0;
            for(int r=0; r<2*n; r++){
                // 跨度超过n
                if(r-l>=n){
                    sum = sum - arr[l];
                    l++;
                    while(arr[l]<0 && l<r){
                        sum = sum - arr[l];
                        l++;
                    } 
                }
                // l超过n
                if(l>n-1) break;
                
                // 基本逻辑
                sum = sum + arr[r];
                max = Math.max(max, sum);

                // sum<0, 重置l
                if(sum<0){
                    sum =0;
                    l=r+1;
                }
            }
            
            System.out.println(max);
        }

    }
}


编辑于 2023-04-02 03:49:59 回复(0)
import sys

n=int(input())
for _ in range(n):
    N=int(input())
    A=list(map(int,sys.stdin.readline().strip().split()))
    s=sum(A)
    sum1=0
    sum2=0
    ans=0
    for num in A:
        if sum1>=0:
            sum1+=num
        else:
            sum1=num
        if sum2<=0:
            sum2+=num
        else:
            sum2=num
        ans=max(ans,sum1)
        ans=max(ans,s-sum2)
    print(ans)


python3动态规划,对最大连续子数组和和数组和减去最小连续子数组和取最大值。非常气愤的是python2会超时,python3不会。
发表于 2023-03-13 22:57:42 回复(0)
//1 设想最大连续队列不跨环 那么就是常规求动态规划问题 可得一个最大连续值
//2.设想最大连续队列跨环,那么就是求最小连续不跨环和问题(跨环最大值=数组总和-最小不跨环总和) 可得一个最大值
//3.比较它们俩的最大值,返回,总共2种情况,属于分类讨论了

package 2021校招第9.寿司;  import java.util.Scanner;  public class Main { public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);  int groups = scanner.nextInt();  for (int i = 0; i < groups; i++) { int n = scanner.nextInt();  int[] nums = new int[n];  for (int j = 0; j < n; j++) {
                nums[j] = scanner.nextInt();  }
            System.out.println(solution(nums,n));  }
    } public static int solution(int[] nums,int n){ int[] dpWithLoop = new int[n];  int[] dpWithOutLoop = new int[n];  int sum = 0;  for (int i = 0; i < nums.length; i++) {
            sum+=nums[i];  } int maxWithLoop = 0;  int maxWithOutLoop = 0;  dpWithOutLoop[0] = nums[0];  dpWithLoop[0] = nums[0];  if(dpWithOutLoop[0]>maxWithOutLoop){
            maxWithOutLoop = dpWithOutLoop[0];  } if(sum-dpWithLoop[0]>maxWithLoop){
            maxWithLoop = sum-dpWithLoop[0];  } for (int i = 1; i < nums.length; i++) {
            dpWithOutLoop[i] = Math.max(nums[i],nums[i]+dpWithOutLoop[i-1]);  if(dpWithOutLoop[i]>maxWithOutLoop){
                maxWithOutLoop = dpWithOutLoop[i];  }
        } for (int i = 1; i < nums.length; i++) {
            dpWithLoop[i] = Math.min(nums[i],nums[i]+dpWithLoop[i-1]);  if(sum-dpWithLoop[i]>maxWithLoop){
                maxWithLoop = sum-dpWithLoop[i];  }
        } return Math.max(maxWithLoop,maxWithOutLoop);  }
}

发表于 2022-10-07 18:45:52 回复(0)
我来一个java版的单调队列+前缀和+窗口滑动:借用上面的思路:sum[j]-min(sum[i])得到最大窗口值,min(sum[i])通过单调栈来维护,同时需要维护窗口宽度
package main;

import java.io.*;
import java.util.Deque;
import java.util.LinkedList;


public class Main{
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
        int T=Integer.parseInt(br.readLine().trim());
        for(int i=0;i<T;i++){
            int N=Integer.parseInt(br.readLine().trim());
            String[] str=br.readLine().trim().split(" ");
            int[] A=new int[N];
            for(int j=0;j<N;j++){
                A[j]=Integer.parseInt(str[j]);
            }
            int[] nums=new int[2*N];//拼接数组,将环展开
            System.arraycopy(A,0,nums,0,A.length);
            System.arraycopy(A,0,nums,A.length,N);
            int[] sum=new int[2*N+1];
            for(int j=1;j<=2*N;j++){//前缀和
                sum[j]=sum[j-1]+nums[j-1];
            }
            int ans=Integer.MIN_VALUE;
            Deque<Integer> deque=new LinkedList<>();
            //维护一个单调栈,里面存最小的前缀和
            for(int right=0;right<=nums.length;right++){
                while(!deque.isEmpty()&&sum[deque.peekLast()]>=sum[right]){
                    deque.removeLast();
                }
                deque.addLast(right);
                int widgth=right-deque.peekFirst();//维护窗口宽度
                if(widgth>N){
                    deque.removeFirst();
                }
                ans=Math.max(ans,sum[right]-sum[deque.peekFirst()]);
            }
            bw.write(String.valueOf(ans));
            bw.newLine();
            bw.flush();
        }
        bw.close();
        br.close();
    }
}









发表于 2022-08-11 22:33:58 回复(0)
求救!!!
我不知道我这段代码错哪了,调试不出原因,总有两个用例过不去555
package main

import "fmt"

func MaxScore() {
	var group,N int
	fmt.Scan(&group)
	for{
		_,err:=fmt.Scan(&N)
		if err !=nil{
			return
		}
		A:=make([]int,N)
		for i:=0;i<N;i++{
			fmt.Scan(&A[i])
		}

	fmt.Println(findScore(A))
	}
}

func findScore(nums []int) int {
	var Maxscore,TmpScore,MinScore int
	var total int
	Length:=len(nums)
	if Length==0{
		return 0
	}
	for i:=0;i<Length;i++{
		total+=nums[i]
		if nums[i]>0{
			TmpScore+=nums[i]
			continue
		}
		if nums[i] < 0&&TmpScore!=0 {
			if Maxscore<TmpScore{
				Maxscore=TmpScore
			}
			if TmpScore<0 {
				TmpScore=0
			}else {
				TmpScore+=nums[i]
			}
		}
	}
	TmpScore=0
	for i:=0;i<Length;i++{
		if nums[i]<0{
			TmpScore+=nums[i]
			continue
		}
		if nums[i]>0&&TmpScore!=0 {
			if MinScore>TmpScore {
				MinScore=TmpScore
			}
			if TmpScore>0{
				TmpScore=0
			}else {
				TmpScore+=nums[i]
			}

		}
	}

	if Maxscore<total-MinScore{
		return total-MinScore
	}
	return Maxscore
}

func main() {
	MaxScore()
}


发表于 2022-05-21 17:53:21 回复(0)
//第一个回答的思路+贪心
#include<iostream>
using namespace std;
int main(){
    int n;
    cin>>n;
    while(n--){
        int len,sum_min=0,sum_max=0,sum=0,min=1000,max=-1000;
        cin>>len;
        while(len--){
            int tmp;
            cin>>tmp;
            sum+=tmp;
            sum_max+=tmp;
            sum_min+=tmp;
            max=max>sum_max?max:sum_max;
            min=min<sum_min?min:sum_min;
            if(sum_max<0)
                sum_max=0;
            if(sum_min>0)
                sum_min=0;
        }
        int bigger=max>sum-min?max:sum-min;
        cout<<bigger<<endl;
    }
    return 0;
}

发表于 2022-03-07 00:05:07 回复(0)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int t;
    cin >> t;
    while(t--) {
        int n;
        int ans = 0;
        cin >> n;
        vector<int>v(n), lrdp(n, INT_MIN), rldp(n, INT_MIN), lr(n, 0), rl(n, 0), lrmax(n, 0), rlmax(n+1, 0);
         
        for(int i = 0; i < n; i++) {
            cin >> v[i];
            lr[i] = i == 0? v[i]: v[i] + lr[i-1];
            lrmax[i] = i == 0 ? max(0, lr[i]) : max(lrmax[i-1], lr[i]);
        }
        for (int i = n - 1; i >= 0; --i) {
            rl[i] = i == n-1 ? v[i] : v[i] + rl[i + 1];
            rlmax[i] = i == n-1 ? max(0, rl[i]) : max(rlmax[i + 1], rl[i]);
        }
        lrdp[0] = v[0];
        ans = max(lrdp[0], ans);
        for(int i = 1; i < n; ++i) {
            lrdp[i] = lrdp[i-1] > 0 ? lrdp[i-1] + v[i]: v[i];
            ans = max(ans, lrdp[i]);
        }
        rldp[n-1] = v[n-1];
        ans = max(ans, rldp[n-1]);
        for(int i = n-2; i >= 0; i--) {
            rldp[i] = rldp[i+1] > 0 ? rldp[i+1] + v[i]: v[i];
            ans = max(rldp[i], ans);
        }
        for (int i = 0; i < n; i++) {
            ans = max(ans, lrmax[i] + rlmax[i+1]);
        }
         
        cout << ans << endl;
         
    }
}

发表于 2022-01-09 17:44:48 回复(0)
1. 破环成链, 计算链上所有长度小于等于N的区间的最大值即可
2. 使用前缀和技巧计算区间和, 对于i位置, 需要计算以i位置为末尾的
   最大子区间和的话, 只需找到前缀和最小的j位置, s[i] - s[j]即为所求
3. 枚举每个位置, 对于当前位置, 可行前缀在一个长度为N的滑动窗口内部
4. 使用单调队列求滑动窗口内的最小值

整体算法的时间复杂度 O(N) 
(如果使用优先队列实现查找滑动窗口内的前缀最小值则时间复杂度为 O(NlogN))
#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
int w[N], s[N];


void solve(){
    deque<int> dq;
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        cin >> w[i];
        w[i + n] = w[i];
    }
    for (int i = 1; i <= 2 * n; i ++ )
        s[i] = s[i - 1] + w[i];
    dq.push_front(0);
    int ans = 0;

    for (int i = 1; i <= 2 * n; i ++ ) {
        if (i >= dq.front() + n)
            dq.pop_front();
        if (dq.size()) {
            ans = max(ans, s[i] - s[dq.front()]);
            // cout << i << ' ' << s[i] << ' ' << ans << ' ' << dq.front() << ' ' << s[dq.front()] << endl;
        }
        while (dq.size() and s[dq.back()] > s[i])
            dq.pop_back();
        dq.push_back(i);
    }
    cout << ans << endl;    
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t -- )
        solve();
    return 0;
}



编辑于 2021-10-15 21:36:46 回复(0)
O(n),MaxSubarray的变种,1-n的最大array和1-n的最小array对称问题可以找到答案。
//
//  10.cpp
//  练习
//
//  Created by Ekko on 2021/9/12.
//

#include <iostream>
using namespace std;
int main(){
    int T, N;
    int arr[100000];
    cin >> T;
    int res;
    int right;
    int res1;
    int tmp;
    for(int i = 0; i < T; i++){
        cin >> N;
        for(int j = 0; j < N; j++){
            cin >> arr[j];
        }
        if(arr[0] > 0){
            res = arr[0];
        }
        else{
            res = 0;
        }
        res1 = arr[0];
        for(int j = 1; j < N; j++){
            if(res1 <= 0){
                res1 = arr[j];
            }
            else{
                res1 += arr[j];
            }
            if(res1 > res){
                res = res1;
            }
        }
        if(arr[0] < 0){
            tmp = arr[0];
        }
        else{
            tmp = 0;
        }
        res1 = arr[0];
        for(int j = 1; j < N; j++){
            if(res1 >= 0){
                res1 = arr[j];
            }
            else{
                res1 += arr[j];
            }
            if(res1 < tmp){
                tmp = res1;
            }
        }
        res1 = 0;
        for(int i = 0; i < N; i++)
            res1 += arr[i];
        res1 = res1 - tmp;
        if(res1 > res)
            res = res1;
        cout << res << '\n';
    }
}



发表于 2021-09-13 23:05:02 回复(0)
python动态规划,两种情况求最大值
T = int(input())
while T>0:
    N = int(input())
    food = list(map(int,input().split()))
    dp = [0]*N
    dp[0] = food[0]
    for i in range(1,N):
        dp[i] = max(dp[i-1]+food[i],food[i])
    res_one = max(dp)
    summry = sum(food)
    dp_new = [0]*N
    dp_new[0] = food[0]
    for i in range(1,N):
        dp_new[i] = min(dp_new[i-1]+food[i],food[i])
    res_two = summry - min(dp_new)
    print(max(res_one,res_two))
    T-=1

发表于 2021-05-24 08:36:23 回复(0)
def solve(N,l):
    p=[0]
    for i in range(N):
        p.append(max(l[i],l[i]+p[i]))
    return max(p)
T=int(input())
while T:
    T-=1
    n=int(input())
    l=[int(i) for i in input().split()]
    a=solve(n,l)
    b=solve(n,[-i for i in l])
    c=max(sum(l)+b,a)
    print(c)

发表于 2021-04-13 21:20:30 回复(0)