POJ 3017 Cut the Sequence (单调队列优化dp)
首先我们可以推出朴素的dp转移方程 dp[i]=min(dp[j])+max(a[j+1]...a[i])
但是这个转移方程显然是超时的,所以我们需要对转移方程进行优化
做这种题目的时候,我们需要对方程进行观察,我们发现,我们需要保存两种值,一个是前面已经算过的dp方程,这个很简单
另一个是一段满足题目条件的a[i],就是最大值的关系,继而我们可以发现,如果我们对方程进行优化,我们删除的内容是要不可能取到的
换句话来说,也就是我们需要删除 s[i]-s[头部]>m的所有值,这是从头部删除数
还有队列中比a[i]小的数,因为在这个数之前的所有dp状态都取不到,我们取最大值是最优的
之后我们还需枚举从这个最大值到队末的状态,因为dp值虽然变大,但是a[i]是减小的
代码中有注释。
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<vector> #include<string> #include<cstring> #include<map> using namespace std; typedef long long ll; const int N=1e5+10; int q[N]; ll a[N]; ll s[N]; ll f[N]; int main(){ int i; int n; ll m; cin>>n>>m; for(i=1;i<=n;i++){ scanf("%d",&a[i]); s[i]=s[i-1]+a[i]; } int tt=-1; int hh=0; int sign=0; int flag=0; for(i=1;i<=n;i++){ while(i>sign&&s[i]-s[sign]>m)//将不满足条件的值都删掉,找到一个满足条件的位置 sign++; if(sign==i){ flag=1; break; } while(hh<=tt&&a[i]>=a[q[tt]])//删除队列末小于等于他的值 tt--; q[++tt]=i; while(hh<=tt&&s[i]-s[q[hh]-1]>m) //删除队头不满足条件的值 hh++; f[i]=f[sign]+a[q[hh]]; for(int j=hh;j<tt;j++){ //枚举最大值之后的所有情况 f[i]=min(f[i],f[q[j]]+a[q[j+1]]); } } if(flag==1) cout<<-1<<endl; else cout<<f[n]<<endl; }
没有人不辛苦,只有人不喊疼