【计蒜客 - 蓝桥训练】蒜厂年会(循环数列的最大子段和)

题干:

在蒜厂年会上有一个抽奖,在一个环形的桌子上,有 nn 个纸团,每个纸团上写一个数字,表示你可以获得多少蒜币。但是这个游戏比较坑,里面竟然有负数,表示你要支付多少蒜币。因为这些数字都是可见的,所以大家都是不会出现的赔的情况。

游戏规则:每人只能抓一次,只能抓取一段连续的纸团,所有纸团上的数字和就是你可以获得的蒜币。

蒜头君作为蒜厂的一员在想,我怎么可以获得最多的蒜币呢?最多能获取多少蒜币呢?

因为年会是发奖,那么一定有大于 00 的纸团。

输入格式
第一行输入一个整数 nn,表示有 nn 个纸团。

第二行输入输入 nn 个整数 a_iai​,表示每个纸团上面写的数字(这些纸团的输入顺序就是环形桌上纸团的摆放顺序)。

输出格式
输出一个整数,表示蒜头君最多能获取多少蒜币。

数据范围
对于 30\%30% 的数据:1 \le n \le 10^2,-10^3 \le a_i \le 10^31≤n≤102,−103≤ai​≤103。

对于 60\%60% 的数据:1 \le n \le 5 \times 10^3,-10^6 \le a_i \le 10^61≤n≤5×103,−106≤ai​≤106。

对于 100\%100% 的数据:1 \le n \le 10^5,-10^9 \le a_i \le 10^91≤n≤105,−109≤ai​≤109。

输入

3
1 -2 1

输出

2

          这题一开始以为就是一道简单的最大连续上升子序列问题,但是ni,wa了,这题还有一个限制条件就是这个所求的序列的起点和终点的长度不能够大于n,,,,,这就是这题的困难之处了。

          依据最大连续上升子序列的问题的解题方法,可以先去求一下前缀和,剩下的就是用优先级队列去处理了,用优先级队列可以干啥馁,我们可以定义一个结构体去存放当前的下标和前缀和,那么这个队列需要维护的是一个最小堆,即前缀和从小到大排序,如果遇到相同的值的话就按照下标从小到大去排列。

        为啥呢,因为肯定是减去一个越小的值所得到的连续和最大,对吧?,然后这个前提就是看这个区间从长度是不是小于等于n,如果是,那就判断答案就行了

#include <iostream>
#include<algorithm>
#include <string>
#include<map>
#include<cstring>
#include<cmath>
#include<queue>
#include <cstdio>
#include <vector>
#include<set>
using namespace std;
typedef long long ll;
const int N= 1e6+10;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int MOD=998244353;
#define rep(i,a,b) for(int i=a;i<=b;i++)
int n;
ll a[N];
struct node{
	ll val;
	int id;
	bool operator < (const node &a) const {
		if(val==a.val) return id>a.id;
		return val>a.val;
	}
//	node() {}
//	node (ll vv,int idv) : val(vv),id(idv) {}
};
priority_queue<node>p;
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	int n;
	cin>>n;
	rep(i,1,n) cin>>a[i];
	rep(i,n+1,n*2) a[i]=a[i-n];
	rep(i,1,n*2){
		a[i]+=a[i-1];
	}
	ll ans=a[1];
	ll res=a[1];
	for(int i=1;i<=2*n;i++){
		if(p.empty()) res=0;
		while(!p.empty()){
			node u=p.top();
			if(i-u.id+1>n) {
			p.pop();//如果当前的区间长度大于n了,那么以后的肯定也会大于n,因此就将这个数值从队列中去除 
			continue;
			}
			res=a[i]-u.val;
		    ans=max(ans,res);
			break;
		}
		p.push(node{a[i],i});
	}
	cout<<ans<<endl;
	return 0;
}

或者这样去做https://blog.csdn.net/qq_41410799/article/details/87436835

#include <iostream>
#include<algorithm>
#include <string>
#include<map>
#include<cstring>
#include<cmath>
#include<queue>
#include <cstdio>
#include <vector>
#include<set>
using namespace std;
typedef long long ll;
const int N= 1e6+10;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int MOD=998244353;
#define rep(i,a,b) for(int i=a;i<=b;i++)
int n;
ll a[N];
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	int n;
	cin>>n;
	ll sum=0; 
	rep(i,1,n) cin>>a[i],sum+=a[i];;
	ll minsum=a[1],maxsum=a[1];
	ll minvt=0,maxvt=0;
	for(int i=1;i<n;i++){
		minvt+=a[i];
		maxvt+=a[i];
		minsum=min(minsum,minvt);
		maxsum=max(maxsum,maxvt);
		if(minvt>0) minvt=0;
		if(maxvt<0) maxvt=0;
	}
	cout<<max(maxsum,sum-minsum)<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值