关于序列中问题
一般都是dp[i]表示以第i个字符为结束的【某问题】(如连续最大值,连续序列之和大于0的最大长度,不递减序列之和的最值)的值,都是以这个为基础出发,然后求整体的最值。
有感而发吧
干物妹小埋 (不递减序列之和的最大值)
题目内容
思路:h[i]是桌子的高,找到前面比比他矮的最大值max{ dp[j] + happy[i] } ( j < i , h[j] <= h[i] ) 可以用线段树,树状数组优化,
线段树实现:
#include <bits/stdc++.h> using namespace std; const int maxn=2e5+5; const int maxnn=1e6+5; int mod=1e9+7; #define pi 3.14159265357 #define ll long long #define IOS cin.sync_with_stdio(false);cin.tie(0);cout.tie(0); #define pb push_back #define mp make_pair //#define fio(n) (int i=0; i<n; ++i) //#define fjo(n) (int j=0; j<n; ++j) ll b[maxn],sum[maxn<<2]; int h[maxn]; set<int> s; map<int,int> mmp; set<int> ::iterator it; void updata(int rt,int l,int r,int pos,ll val){ if(l==r){ sum[rt]=val; return ; } //cout<<1<<endl; int mid=(l+r)>>1; if(pos<=mid) updata(rt<<1,l,mid,pos,val); else updata(rt<<1|1,mid+1,r,pos,val); sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); return ; } ll query(int rt,int l,int r,int x,int y){ ll ans=-1; //cout<<2<<endl; if(x<=l&&r<=y){ return sum[rt]; } int mid=(l+r)>>1; if(x<=mid) ans=max(ans,query(rt<<1,l,mid,x,y)); if(y>mid) ans=max(ans,query(rt<<1|1,mid+1,r,x,y)); return ans; } int main() { std::ios::sync_with_stdio(false); int n; cin>>n; memset(sum,0,sizeof(sum)); for(int i=0; i<n; ++i){ cin>>h[i]; s.insert(h[i]); } int tot=0; for(it=s.begin(); it!=s.end(); ++it){ mmp[*it]=++tot;//把高度离散化 } ll ans=-1; for(int i=0; i<n; ++i) cin>>b[i]; for(int i=0; i<n; ++i){ int pos=mmp[h[i]]; ll tmp=query(1,1,tot,1,pos);//找到前面 <=h[i] 的最大happy值,因为是按顺序插入的可以保证 当前树中的值都是i前面的 //cout<<tmp<<endl; tmp+=b[i]; ans=max(ans,tmp); //cout<<sum[6]<<endl; //cout<<ans<<" "<<tmp<<endl; updata(1,1,tot,pos,tmp);//更新当先位置的这个高度的最大happy值 } cout<<ans<<endl; return 0; }
数组数组:
#include <bits/stdc++.h> using namespace std; const int maxn=2e5+5; const int maxnn=1e6+5; int mod=1e9+7; #define pi 3.14159265357 #define ll long long #define IOS cin.sync_with_stdio(false);cin.tie(0);cout.tie(0); #define pb push_back #define mp make_pair //#define fio(n) (int i=0; i<n; ++i) //#define fjo(n) (int j=0; j<n; ++j) ll b[maxn],sum[maxn<<2]; int h[maxn]; int n; set<int> s; map<int,int> mmp; set<int> ::iterator it; inline int lowbit(int x){ return x&(-x); } inline void updata(int x,ll val){ for(int i=x; i<=n; i+=lowbit(i)) sum[i]=max(sum[i],val); } ll query(int l,int r){ ll ans=-1; int mid=r-lowbit(r)+1; int mm=l+lowbit(l); if(l==mid) return sum[r]; if(mid>l) return max(query(l,mid-1),sum[r]); else return max(sum[mm],query(mm+1,r)); } int main() { std::ios::sync_with_stdio(false); cin>>n; memset(sum,0,sizeof(sum)); for(int i=0; i<n; ++i){ cin>>h[i]; s.insert(h[i]); } int tot=0; for(it=s.begin(); it!=s.end(); ++it){ mmp[*it]=++tot; } ll ans=-1; for(int i=0; i<n; ++i) cin>>b[i]; for(int i=0; i<n; ++i){ int pos=mmp[h[i]]; //cout<<pos<<endl; ll tmp=query(1,pos); //cout<<tmp<<endl; tmp+=b[i]; ans=max(ans,tmp); //cout<<sum[6]<<endl; //cout<<ans<<" "<<tmp<<endl; updata(pos,tmp); } cout<<ans<<endl; return 0; }
小阳买水果 (连续序列之和大于0的最大长度)
题目描述 水果店里有 n n个水果排成一列。店长要求顾客只能买一段连续的水果。 小阳对每个水果都有一个喜爱程度 a i ai,最终的满意度为他买到的水果的喜欢程度之和。 如果和为正(不管是正多少,只要大于 0 0 即可),他就满意了。 小阳想知道在他满意的条件下最多能买多少个水果。 你能帮帮他吗? 输入描述: 第一行输入一个正整数 n,表示水果总数。 第二行输入 n 个整数 a i ai,表示小阳对每个水果的喜爱程度。 输出描述: 一行一个整数表示结果。(如果 1 个水果都买不了,请输出 0) 示例1 输入 5 0 0 -7 -6 1 输出 1 备注:1≤n≤2×10e6,|ai|≤10e3
思路:前缀和a[i],然后找比a[i]小的最前面的的那个位置max{ i - j } ( j < i , a[j] < a[i] ) 可以用线段树,树状数组优化,亦可以排一下序然后比较一下(看别人的)
把前缀和从小到大排序,如果前缀和相等把编号大的放前面,然后从前往后遍历,记录遍历过程最小的编号,如果当前的编号大于最小的编号,则更新答案。注意特判n=1的情况就可以View Code#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e6+7; int n; struct node{ int pos,num; }p[maxn]; bool cmp(node a,node b){ if(a.num==b.num) return a.pos>b.pos; return a.num<b.num; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x; scanf("%d",&x); p[i].num=p[i-1].num+x; p[i].pos=i; } n++; int minpos=n; int ans=0; sort(p+1,p+n+1,cmp); for(int i=1;i<=n;i++){ minpos=min(minpos,p[i].pos); //记录编号最小的位置 if(minpos<p[i].pos) //当前编号大于记录的最小编号,则更新答案 ans=max(ans,p[i].pos-minpos); } printf("%d\n",ans); return 0; }
待续。。。