对于单调性优化其实更多的是观察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. git笔记-9-29

    //将工作区的a.txt文件更新到最后一次提交到本地仓库的状态,如果需要将文件回滚到某个特定的版本,将HEAD改成那个commit的id即可 git checkout HEAD a.txt //如果将 ...

  2. Maven包下载不下来的情况

    从svn上遇到过项目下载下来,缺丢失了一些包,怎么都下载不了,只能从同事的电脑上给拷贝下来? 千万别这样,别问为何,说多了都是泪,然后发现. 如果是eclipse的话: 勾选这两个选项,就能下载下来了 ...

  3. 【iCore1S 双核心板_ARM】例程十三:SDIO实验——读取SD卡信息

    实验现象: 核心代码: int main(void) { /* USER CODE BEGIN 1 */ HAL_SD_TransferStateTypedef State; /* USER CODE ...

  4. Guava Lists.transform踩坑小记<转>

    1.问题提出 1.前段时间在项目中用到Lists.transform返回的List,在对该list修改后发现修改并没有反映在结果里,研究源码后发现问题还挺大.下面通过单步调试的结果来查看Guava L ...

  5. Extjs4 DateTimeField,日期时间控件完美版

    网上若干类似的控件,要么是有bug,要么是操作体验不合理,这里贡献一个比较科学的版本. 扩展包下载: http://files.cnblogs.com/qidian10/Ext.ux.rar 解压至E ...

  6. Java知多少(10)数据类型及变量

    Java 是一种“强类型”的语言,声明变量时必须指明数据类型.变量(variable)占据一定的内存空间.不同类型的变量占据不同的大小. Java中共有8种基本数据类型,包括4 种整型.2 种浮点型. ...

  7. java二叉树字典查询(qq 928900200)

    This assignment will help you practice and understand better the Binary Tree and Binary Search Tree ...

  8. jquery 选择对象随心所欲,遍历数组更是易如反掌

    jquery只要研究总结透彻了,那选择对象就会随心所欲,遍历数组更是易如反掌.选对对象,才能“娶妻生子”,才能有后续的数据处理.呵呵遍历对很关键. 怕只怕,学东西浅尝辄止一知半解.本篇特别研究总结jq ...

  9. Java如何拆分正则表达式和字符串?

    在Java编程中,如何拆分正则表达式和字符串? 以下示例演示如何使用regex.Pattern类的Pattern.compile()方法和patternname.split()方法拆分正则表达式. p ...

  10. Guava学习笔记(三):集合

    添加Maven依赖 ListsTest import com.google.common.collect.Lists; import org.hamcrest.core.Is; import org. ...