对于单调性优化其实更多的是观察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. Serializable接口

    Serializable这个接口起啥作用呢?? 这个接口没有提供任何方法,我们实现它有什么意义呢? Serializable接口是启用其序列化功能的接口.Serializable接口中没有任何方法,一 ...

  2. 关于Unity中的Mesh Collider碰撞器

    原来我的场景中有一个平面Plane带Mesh Collider碰撞器组件,一个主角Hero带有一个Box Collider碰撞器和有重力的Rigidbody刚体组件,主角可以放在平面上. 在导入场景后 ...

  3. Hadoop、Spark 集群环境搭建

    1.基础环境搭建 1.1运行环境说明 1.1.1硬软件环境 主机操作系统:Windows 64位,四核8线程,主频3.2G,8G内存 虚拟软件:VMware Workstation Pro 虚拟机操作 ...

  4. Nginx系列二:(Nginx Rewrite 规则、Nginx 防盗链、Nginx 动静分离、Nginx+keepalived 实现高可用)

    一.Nginx Rewrite 规则 1. Nginx rewrite规则 Rewrite规则含义就是某个URL重写成特定的URL(类似于Redirect),从某种意义上说为了美观或者对搜索引擎友好, ...

  5. 怎样从Javaproject师成长为架构师?

      工作1-5年.当我们向老板提出加薪的时候,或者跳槽去"捡"offer的时候.我们底气够吗? 敢不敢不给涨薪就"挥一挥衣袖.不带走一个bug"?是不是提出要求 ...

  6. Oracle DBA神器之Toad

    很早就听说Toad功能很强大,一直没有使用过,因为PLSQL Developer就很好用.前几天看见同事优化Oracle就是用的Toad,有一些很强大的管理功能,于是再一次对Toad产生兴趣,收集了一 ...

  7. PISQLDAS 查询语句

    SELECT tag,CAST(value AS Float64) FROM piarchive..piavg WHERE tag = ? AND time >= DATE(?) AND tim ...

  8. SpringSecurity兑现多登录成功页面和登录成功返回被拦截界面

    SpringSecurity实现多登录成功页面和登录成功返回被拦截界面 使用SrpingSceurity作为认证和授权的安全框架可以省下很多基础工作. 具体可以参考SpringSecurity,这里不 ...

  9. Linux systemd limits

    https://www.cnblogs.com/IMxY/p/8941022.html limits 关于Centos 7 / RHEL 7 中的limits要了解以下几点: CentOS 7 / R ...

  10. H5 (webApi) 接口帮助文档

    只有你想不到,没有找不到的,强大的 webApis!!! https://developer.mozilla.org/en-US/docs/Web/API