只能说是代码美如画。(当然我是指内在逻辑不是我的代码风格,队长看到要理解哈,啊哈哈哈哈哈哈哈哈)
正常思路咯,f[i]=f[j]+max(a[j+1],a[j+2]....a[i]),枚举j,显然硬来会超时,所以需要有一个单调队列来时时把最大值尽快弄出来并且需要一些剪枝;
剪枝条件有两个,一个是和不能超过m,一个是显然f[i]是个非严格递增序列,根据这两个条件剪枝;
则建立单调队列,每当插入新的i时将前面先把和小于等于m的条件做好,然后对于j<i,如果a[j]<a[i]就可以把j丢弃,那么显然新加入的元素会成为单调队列中最小的,则单调队列为递减序列。
然后就看代码吧
1 #include <cstdio> 2 #include <cstring> 3 #include <set> 4 #include <iostream> 5 #include <map> 6 #include <math.h> 7 #include <algorithm> 8 #include <vector> 9 using namespace std; 10 11 typedef long long ll; 12 13 const int N=100005; 14 const long long INF=1000000000ll; 15 int n; 16 ll m; 17 ll a[N],f[N]; 18 ll solve(){ 19 int i,j,k=0,fg=0,l=0,r=0; 20 int q[N]; 21 ll sum=0; 22 for(i=1;i<=n;i++){ 23 sum+=a[i]; 24 while(sum>m) sum-=a[++k]; 25 if(k>=i){ 26 fg=1; 27 break; 28 } 29 while(l<=r&&a[q[r]]<=a[i]) r--;//等于号是细节 30 q[++r]=i; 31 while(l<=r&&q[l]<=k) l++; 32 ll tmp; 33 int kk=k; 34 f[i]=INF; 35 //cout<<l<<" "<<r<<" "<<kk<<endl; 36 for ( j=l; j<=r; j++) //队列优化后的dp过程,加入过程保证了单调队列中元素是递减的 37 { 38 tmp=f[kk]+a[q[j]]; 39 //cout<<tmp<<" "<<f[i]<<" "<<i<<" "<<kk<<endl; 40 if (tmp<f[i]) f[i]=tmp; 41 kk=q[j]; //kk表示当前最值存在的区间范围是[kk+1,i] 42 } 43 //f[i]=tmp; 44 //cout<<f[i]<<" "<<i<<endl; 45 } 46 if(fg) 47 return -1; 48 else 49 return f[n]; 50 } 51 int main() 52 { 53 freopen("in.txt","r",stdin); 54 cin>>n>>m; 55 int i; 56 for(i=1;i<=n;i++) 57 scanf("%lld",&a[i]); 58 cout<<solve()<<endl; 59 return 0; 60 }
时间: 2024-12-16 21:35:19