cf 1077 区间dp 双向队列保存区间最大值
http://codeforces.com/contest/1077/problem/F2
题意:给定n,k,x; 表示长为n的序列中,每长为k的一段必须有一个数被选择
在选择x次之后,得到的最大值
思路:简单的n,k,x<=200的时候 可以nkx的dp
但是对于nkx<=4000的 这样就超市
但是我们可以上面的思路修改一下
d[i][j]表示 第i次选择,选择最后面的位置为j 这时候的最大值
d[0][0]=0; for(int i=1;i<=x;++i){ for(int j=1;j<=n;++j) for(int q=1;q<=k;++q) if(j-q<0)continue; else if(d[i-1][j-q]!=-1) d[i][j] = max(d[i-1][j-q]+a[j],d[i][j]); }
上面是nkx的复杂度
针对于内层循环,我们可以改进一下:
内层选择的 : 在符合<=k的条件下,取出这个区间的最大值
那么如果用双向队列(i前面的数已经入队)
1.那么把 j-Q.front()>k 的进行pop_back(),这样剩下的都是符合条件的了
2.那如何保证一个最大值呢?
假设我们这次走到了 d[i][j] 那么应该讲 d[i-1][j-1]push进去
如果在此之前 将尾部所有比d[i-1[j-1]的 元素pop_back()
在最初 deque是个递减的 那么这样叠加下去,仍然是递减的
所以deque里的头部永远是这一段的最大值
#include<bits/stdc++.h> using namespace std; #define ll long long #define pb push_back #define mp make_pair #define pii pair<int,int> #define time __time const int N =2e5+4; ll a[N],b[N]; ll d[5222][5222]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int n,x,k; cin>>n>>k>>x; for(int i=1;i<=n;++i)cin>>a[i]; if( n/k>x ){ printf("-1\n"); return 0; } memset(d,-1,sizeof(d)); d[0][0]=0; deque<int>Q; for(int i=1;i<=x;++i){ for(int j=1;j<=n;++j){ while(!Q.empty() &&j-Q.front()>k ) Q.pop_front(); while(!Q.empty() &&d[i-1][Q.back()] <= d[i-1][j-1] ) Q.pop_back(); Q.push_back(j-1); if(d[i-1][Q.front()]!=-1) d[i][j] = d[i-1][Q.front()]+a[j]; } while(!Q.empty())Q.pop_back(); } ll ans = -1; for(int q=n;q>n-k;--q){ ans = max(ans,d[x][q]); } cout<<ans<<endl; return 0; }