对于单调性优化其实更多的是观察dp的状态转移式子的单调性 进而用优先队列 单调队列 二分查找什么的找到最优决策 使时间更优。

对于这道题就是单调性优化的很好的例子

首先打一个暴力再说。

f[i][j]表示前i个木匠刷前j个木板所得到的最大价值

f[i][j]=max(f[i][j],f[i-1][j]);

f[i][j]=max(f[i][j],f[i][j-1])

f[i][j]=max(f[i][j],f[i-1][k]+(j-k)*r(j));(k<t(j)<=j)&&(j-k<=l(j));

这样的话枚举 i j k 时间复杂度 n^2m;

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define t(x) t[x].target
#define r(x) t[x].receive
#define l(x) t[x].l
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
const int MAXN=,maxn=;
struct wy
{
int l,receive,target;
friend int operator <(const wy x,wy y)
{
return x.target<y.target;
}
}t[MAXN];
int n,m;
int f[maxn][MAXN];//f[i][j]表示前i个木匠刷前j个木板所得到的最大价值
//f[i][j]=max(f[i][j],f[i-1][j]);
//f[i][j]=max(f[i][j],f[i][j-1]);
//f[i][j]=max(f[i][j],f[i-1][k]+(j-k)*r(j));(k<t(j)<=j)&&(j-k<=l(j));
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=m;i++)
{
l(i)=read();
r(i)=read();
t(i)=read();
}
sort(t+,t++m);
//for(int i=1;i<=m;i++)cout<<l(i)<<' '<<r(i)<<' '<<t(i)<<endl;
for(int i=;i<=m;i++)
{
for(int j=;j<=n;j++)
{
f[i][j]=max(f[i][j],f[i-][j]);
f[i][j]=max(f[i][j],f[i][j-]);
if(j>=t(i)&&j-l(i)<t(i))
for(int k=max(,j-l(i));k<t(i);k++)
f[i][j]=max(f[i][j],f[i-][k]+(j-k)*r(i));
}
}
put(f[m][n]);
return ;
}

而 n<=16000 m 100 这就直接T飞了考虑将其优化。

f[i][j]=max{f[i-1][k]-k*r(i)}+j*r(i);

so easy 单调队列优化一下结束 保存决策即可

至于一些细节需要注意 不过单调队列这样优化也算是很常见的吧。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define t(x) t[x].target
#define r(x) t[x].receive
#define l(x) t[x].l
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i][j]表示前i个木匠刷前j个木板所得到的最大价值
//f[i][j]=max(f[i][j],f[i-1][j]);
//f[i][j]=max(f[i][j],f[i][j-1]);
//f[i][j]=max(f[i][j],f[i-1][k]+(j-k)*r(i));(k<t(j)<=j)&&(j-k<=l(j));
//考虑将其优化 f[i][j]=max{f[i-1][k]+j*r(i)-k*r(i)};
//f[i][j]=max{f[i-1][k]-k*r(i)}+j*r(i);
//so easy 单调队列优化一下结束 保存决策即可
const int MAXN=,maxn=;
struct wy
{
int l,receive,target;
friend int operator <(const wy x,wy y)
{
return x.target<y.target;
}
}t[MAXN];
int n,m;
int f[maxn][MAXN];
int q[MAXN],l,r;
int kk(int v,int x){return f[v-][x]-x*r(v);}
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=m;i++)
{
l(i)=read();
r(i)=read();
t(i)=read();
}
sort(t+,t++m);
//for(int i=1;i<=m;i++)cout<<l(i)<<' '<<r(i)<<' '<<t(i)<<endl;
for(int i=;i<=m;i++)
{
l=r=;l=;if(t(i)-l(i)<=)q[++r]=;
for(int j=;j<=n;j++)
{
f[i][j]=max(f[i-][j],f[i][j-]);
if(j>=t(i))
{
while(l<=r&&q[l]<(j-l(i)))l++;
if(l<=r)f[i][j]=max(f[i][j],kk(i,q[l])+j*r(i));
}
if(j>=t(i)-l(i)&&j<t(i))
{
while(l<=r&&kk(i,q[r])<=kk(i,j))--r;
q[++r]=j;
}
}
}
put(f[m][n]);
return ;
}

这道题呢 看完题目就觉得很难我甚至觉得dp不太好写

硬写吧  f[i]表示前i个数字所取得的最小代价和 f[i]=min(f[i],f[j]+a[k]);//k j+1~i

直接转移 复杂度 n^3 因为 要求 a[k] j+1~i 之中的最大值

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i个数字所取得的最小代价和
//f[i]=min(f[i],f[j]+a[k]);//k j+1~i
const int MAXN=;
int n,m,flag;
int a[MAXN],s[MAXN];
ll f[MAXN];
int q[MAXN],l,h;
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++)
{
a[i]=read();
s[i]=a[i]+s[i-];
if(a[i]>m)flag=;
}
if(flag==){put(-);return ;}
for(int i=;i<=n;i++)f[i]=INF*1000000ll;
for(int i=;i<=n;i++)
{
for(int j=;j<i;j++)
{
int maxx=;
for(int k=j+;k<=i;k++)maxx=max(maxx,a[k]);
if(s[i]-s[j]<=m)f[i]=min(f[i],f[j]+maxx);
}
}
put(f[n]);
return ;
}

观察一下式子求最大值我完全可以采用ST表的算法预处理 然后 最终复杂度是n^2 因为要枚举j这个决策点

但是既然是n^2的复杂度我完全可以 采用单调队列来寻找最大值吧 复杂度n^2 还是过不了TAT

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i个数字所取得的最小代价和
//f[i]=min(f[i],f[j]+a[k]);//k j+1~i
const ll MAXN=;
ll n,m,flag;
ll a[MAXN],s[MAXN];
ll f[MAXN];
ll q[MAXN],l,r;
int main()
{
freopen("1.in","r",stdin);
n=read();m=read();
for(ll i=;i<=n;i++)
{
a[i]=read();
s[i]=a[i]+s[i-];
if(a[i]>m)flag=;
}
if(flag==){put(-);return ;}
for(ll i=;i<=n;i++)f[i]=INF*1000000ll;
for(ll i=;i<=n;i++)
{
l=r=;l=;
for(ll k=;k<=i;k++)
{
while(l<=r&&a[k]>=a[q[r]])r--;
q[++r]=k;
}
for(ll j=;j<i;j++)
{
while(q[l]<=j)l++;
if(s[i]-s[j]<=m)f[i]=min(f[i],f[j]+a[q[l]]);
}
}
put(f[n]);
return ;
}

然 考虑终极优化 争取优化到O(n) 或者是 nlogn 不会啊 !

书上是这样说的 对于j(0<=j<i)可能成为最优决策除了需要满足 a[i]-a[j]<=M 还需要满足以下两个条件之一:

1 a[j]=max(a[k])(j<=k<=i) 2 j~i C[i]-C[j]<=M 的最小j

如果两个条件都不满足的话 那么 aj<max(ak) j<=k<=i && j~i C[i]-C[k]<=M

(j<=k<=i)max(a[k])==(j+1<=k<=i)max(a[k]) f[i-1]<=f[j] f[j-1]+max(ak j~i )<=f[i]+max(ak j+1~i)

那么此时 j-1 比 j 更有可能成为最优决策 此时j不可能成为最优决策

所以我们可以维护一个 最早的j 剩下的j 就只能是 单调递增 aj的值单调递减 的东西了 这仅仅是我们从上述两个结论中得来的结论。

那么 这也仅仅是单调性的优化 也就是说一些无用决策将被我们删掉 最后我们只需要 比较这么多单调递减的f[j]+a[k](j+1~i)哪个最小即可

根据以上原理 我们可以筛选出很多不必要的决策 将这些决策舍弃

然后维护单调队列即可 但是 针对状态转移时 针对队列中的每个f值 我们都不知道哪个是最优的所以需要比较。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
#define id(x) q[x].id
#define v(x) q[x].v
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i个数字所取得的最小代价和
//f[i]=min(f[i],f[j]+a[k]);//k j+1~i
const ll MAXN=;
ll n,m,flag;
ll a[MAXN];
ll f[MAXN];
struct wy
{
ll id,v;
}q[MAXN];
ll sum,t,l,r;
int main()
{
freopen("1.in","r",stdin);
n=read();m=read();
for(ll i=;i<=n;i++)
{
a[i]=read();
if(a[i]>m)flag=;
}
if(flag==){put(-);return ;}
for(ll i=;i<=n;i++)
{
sum+=a[i];
while(sum>m)sum-=a[t++];
while(l<=r&&id(l)<t)l++;
while(l<=r&&a[i]>=v(r))r--;
id(++r)=i;
v(r)=a[i];
f[i]=f[t-]+v(l);
for(ll j=l;j<r;j++)f[i]=min(f[i],f[id(j)]+v(j+));
}
//for(long long i=1;i<=n;i++)cout<<f[i]<<' ';
put(f[n]);
return ;
}

这样的话可以直接比较 队列中的某个值 进行状态转移这道题真的很有意思 我想了1天呢。

然后貌似这样的算法是 n^2的 (玄学)比n^2小的多的多 所以可以A掉这道题但是...

为了追求更快的AC我们可以采用set维护一下单调队列中的最小值实现复杂度nlogn

但是这个竟然没 上面那个貌似是n^2的算法要慢的多核心思想找到 最优决策。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i个数字所取得的最小代价和
//f[i]=min(f[i],f[j]+a[k]);//k j+1~i
const ll MAXN=;
ll n,m,flag;
ll a[MAXN];
ll f[MAXN];
ll q[MAXN],l,r,sum,t;
multiset<ll>s;
multiset<ll>::iterator it;
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(ll i=;i<=n;i++)
{
a[i]=read();
if(a[i]>m)flag=;
}
if(flag==){put(-);return ;}
for(ll i=;i<=n;i++)
{
sum+=a[i];
while(sum>m)sum-=a[t++];
while(l<=r&&q[l]<t)
{
if(s.empty()==){l++;continue;}
if((it=s.find(f[q[l]]+a[q[l+]]))!=s.end());s.erase(it);
l++;
}
while(l<=r&&a[i]>=a[q[r]])
{
if(s.empty()==){r--;continue;}
if((it=s.find(f[q[r-]]+a[q[r]]))!=s.end())s.erase(it);
r--;
}
if(l<=r)s.insert(f[q[r]]+a[i]);
q[++r]=i;
f[i]=f[t-]+a[q[l]];
if(s.begin()!=s.end())f[i]=min(f[i],*s.begin());
}
//for(ll i=1;i<=n;i++)cout<<f[i]<<' '<<endl;
put(f[n]);
return ;
}

终于A掉了这道题好题啊 ,这个单调性优化关键是发觉上面的两个引理 也就是单调性的特点 进而优化可选择的决策空间 进而缩短时间。

下面是多重背包的单调性优化 :

首先是 朴素的多重背包

完全当成01背包来写 这个就比较简单了。当时我以为这个复杂度不高的,寒假郑州的集训一道题两次多重背包,

没想到我T飞了。。。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i的空间能拿到的最大价值
const int MAXN=;
int n,m;
int w[MAXN],sum[MAXN],v[MAXN];
int f[MAXN];
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++)
{
sum[i]=read();
w[i]=read();
v[i]=read();
}
for(int i=;i<=n;i++)
{
for(int k=;k<=sum[i];k++)
{
for(int j=m;j>=w[i];j--)
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
}
put(f[m]);
return ;
}

当然 有更优秀的解法 2进制拆分 主要原理是 2^0+2^1+...+2^p+R(剩下来的余数)当然其中p是能达到的最大的p

这一堆数字能拼成任意数字 对其在做01背包的情况下 就能快速得到最优解。

证明: 2^0+2^1+...+2^p 能拼成 任意 m-R之中的任意数字 毋庸置疑想想二进制的表示法。2^0+2^1+...2^p==m-R

而对于 R 因为R<2^p+1 且2^0+2^1+..2^p==2p+1 所以R<=2^0+2^1+...2^p

所以 这堆数字又可以拼成 1~R任意的数字 所以 综上 它是可以拼出1~m之中的任意数字 对每个数字都做一次01背包即可得到最优解

因为是最优解一定会是最优解(犹如是金子总会发光发热 神说要有光嘛)

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i的空间能拿到的最大价值
const int MAXN=;
int n,m;
int w[MAXN],sum[MAXN],v[MAXN];
int f[MAXN];
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++)
{
sum[i]=read();
w[i]=read();
v[i]=read();
}
for(int i=;i<=n;i++)
{
if(sum[i]*w[i]>=m)
for(int j=w[i];j<=m;j++)
f[j]=max(f[j],f[j-w[i]]+v[i]);
else
{
int s=,cnt=sum[i];
while(s<=cnt)
{
for(int j=m;j>=s*w[i];j--)
f[j]=max(f[j],f[j-s*w[i]]+s*v[i]);
cnt-=s;s=s<<;
}
for(int j=m;j>=cnt*w[i];j--)
f[j]=max(f[j],f[j-cnt*w[i]]+cnt*v[i]);
}
}
put(f[m]);
return ;
}

下面是一个单调队列优化的思路 要实现这个我们需把状态转移方程变一变。

f[j]表示前j空间能装的最大价值 f[j]=max{f[j-k*w[i]]+k*v[i]};

由于每次转移 只有倍数之间才会互相转移所以可以写成这样

f[u+p*w[i]]=max{f[u+k*w[i]]+(p-k)*v[i]};

这样使其具有单调性 然后利用单调队列维护最优决策即可 。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<deque>
#include<vector>
#include<stack>
#include<algorithm>
#include<set>
#include<bitset>
#include<map>
#define INF 2147483646
#define ll long long
#define ldb long double
using namespace std;
char buf[<<],*ft,*fs;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?x=-x,putchar('-'):;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar();return;
}
//f[i]表示前i的空间能拿到的最大价值
const int MAXN=;
int n,m;
int w[MAXN],sum[MAXN],v[MAXN];
int f[MAXN];
int k(int i,int u,int p){return f[u+p*w[i]]-p*v[i];}
int q[MAXN],l,r;
int main()
{
freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=n;i++)
{
sum[i]=read();
w[i]=read();
v[i]=read();
}
for(int i=;i<=n;i++)
{
for(int u=;u<w[i];u++)
{
l=;r=;
int p=(m-u)/w[i];
for(int j=p-;j>=max(p-sum[i],);j--)
{
while(l<=r&&k(i,u,q[r])<=k(i,u,j))r--;
q[++r]=j;
}
for(int j=p;j>=;j--)
{
while(l<=r&&q[l]>j-)l++;
f[u+j*w[i]]=max(f[u+j*w[i]],k(i,u,q[l])+j*v[i]);
if(j-sum[i]->=)
{
while(l<=r&&k(i,u,q[r])<=k(i,u,j-sum[i]-))r--;
q[++r]=j-sum[i]-;
}
}
}
}
put(f[m]);
return ;
}

多看 多想 多实践!

dp 单调性优化总结的更多相关文章

  1. dp单调性优化

    跟着书上的思路学习dp的单调性优化觉得还是很容易想的. 数据范围: dp,数据范围是百万,这应该是O(n)的算法了. 首先不难想到设f[i]表示到第i个百米所能达到的最大能量,那么f[n]即为所求. ...

  2. [NOI2009]诗人小G(dp + 决策单调性优化)

    题意 有一个长度为 \(n\) 的序列 \(A\) 和常数 \(L, P\) ,你需要将它分成若干段,每 \(P\) 一段的代价为 \(| \sum ( A_i ) − L|^P\) ,求最小代价的划 ...

  3. Lightning Conductor 洛谷P3515 决策单调性优化DP

    遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...

  4. CF868F Yet Another Minimization Problem 分治决策单调性优化DP

    题意: 给定一个序列,你要将其分为k段,总的代价为每段的权值之和,求最小代价. 定义一段序列的权值为$\sum_{i = 1}^{n}{\binom{cnt_{i}}{2}}$,其中$cnt_{i}$ ...

  5. BZOJ4899: 记忆的轮廓【概率期望DP】【决策单调性优化DP】

    Description 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...

  6. 2018.10.14 NOIP训练 猜数游戏(决策单调性优化dp)

    传送门 一道神奇的dp题. 这题的决策单调性优化跟普通的不同. 首先发现这道题只跟r−lr-lr−l有关. 然后定义状态f[i][j]f[i][j]f[i][j]表示猜范围为[L,L+i−1][L,L ...

  7. 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)

    传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...

  8. 单调性优化DP

    单调性优化DP Tags:动态规划 作业部落链接 一.概述 裸的DP过不了,怎么办? 通常会想到单调性优化 单调队列优化 斜率优化 决策单调性 二.题目 [x] 洛谷 P2120 [ZJOI2007] ...

  9. [BZOJ4850][JSOI2016]灯塔(分块/决策单调性优化DP)

    第一种方法是决策单调性优化DP. 决策单调性是指,设i>j,若在某个位置x(x>i)上,决策i比决策j优,那么在x以后的位置上i都一定比j优. 根号函数是一个典型的具有决策单调性的函数,由 ...

随机推荐

  1. 每日英语:Upgrade Your Life: How to speed up your PC (or Mac)

    Is your desktop or laptop computer starting to feel a little poky? Even after just a few months of u ...

  2. TWELP™ Vocoder

    TWELP™ Vocoder   DSP Innovations Inc. (DSPINI) announces new class of proprietary vocoders for wide ...

  3. Java知多少(28)super关键字

    super 关键字与 this 类似,this 用来表示当前类的实例,super 用来表示父类. super 可以用在子类中,通过点号(.)来获取父类的成员变量和方法.super 也可以用在子类的子类 ...

  4. 三、主流区块链技术特点及Hyperledger Fabric V1.0版本特点

    一.Hyperledger fabric V1.0 架构 1.逻辑架构: 2.区块链网络 3.运行时架构 二.架构总结 1.架构要点 分拆Peer的功能,将Blockchain的数据维护和共识服务进行 ...

  5. Angular4学习笔记(六)- Input和Output

    概述 Angular中的输入输出是通过注解@Input和@Output来标识,它位于组件控制器的属性上方. 输入输出针对的对象是父子组件. 演示 Input 新建项目connInComponents: ...

  6. SpringBatch的初步了解

    一.SpringBatch是一个批处理的框架,作为一个Spring组件,提供了通过使用Spring的依赖注入来处理批处理的条件. 什么是批处理呢? 在现代企业应用当中,面对复杂的业务以及海量的数据,除 ...

  7. python对象池模式

    class QueueObject(): def __init__(self, queue, auto_get=False): self._queue = queue self.object = se ...

  8. iOS - 让WKWebView 支持 NSURLProtocol

    iOS8以后,苹果推出了新框架Webkit,提供了替换UIWebView的组件WKWebView.各种UIWebView的问题没有了,速度更快了,占用内存少了,一句话,WKWebView是App内部加 ...

  9. 官方文档:Office VBA 参考

    https://docs.microsoft.com/zh-CN/office/vba/api/overview/    Office VBA 参考 https://docs.microsoft.co ...

  10. Unity3D Shader 按百分比裁剪模型

       Shader Shader "Unlit/Disappear" { Properties { _MainTex ("Texture", 2D) = &qu ...