总结一下最近几天对dp优化中的四边形不等式的学习。
证明什么的似懂非懂,我还是太年轻了。
第一种, n^2 -> nlogn
例题:http://wzoi.cc/s/1370/1304 (由于权限问题 不公开题面)
就是1个体积均为1-300,100000个物品,做一个100000的背包。
发现体积最多只有 300 种,分开做。对于同种体积的物品,显然按照价值从大到小取最优。
我们假设 s=1,设状态 f[i]为大小为 i的背包的最大收益。
转移函数 f[i]=max(f[ i-j ]+sum(a[1..j]))
是一个 f[x]=min{f[i]+w[i,x]}的经典模型
w满足大区间大于等于小区间的性质,
且当 a<b<c<d时,w[a,c]+w[b,d]<=w[a,d]+w[b,c],
于是 决策有单调性 ,假如用 k(x)表示状态x 取到最优值时的决策 , k(x) 是不降的。
注意到给定两个决策点,我们是可以通过二分计算出令他们大小关系反转的时间点 t 的。
于是就可以维护一个单调栈,进行二分。
对于其他的 s,发现只有在状态模 s 同余时单调性才成立,所以对于每一种体积 s,按照剩余类的顺序依次做即可。
时间复杂度 O(MAXS*K+N)
code :
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,r,l) for(int i=r;i>=l;--i)
template <typename T> void chmin(T &x,const T &y)
{
if(x>y)x=y;
}
template <typename T> void chmax(T &x,const T &y)
{
if(x<y)x=y;
}
const int N=1e6+5,M=1e5+5,S=300+5;
ll dp[S][M];
int t[S];
struct edge
{
int v,next;
}l[N];
ll val[N];int q[N],top;
int st[N],can[N],head,tail;
int s,d,mi,i;
ll get(int j,int i)
{
return dp[s-1][j*s+d]+val[i-j];
}
bool you(int j,int k)
{
return get(i,k)>=get(j,k);
}
#define mid (l+r>>1)
int erfen()
{
if(head==tail) return i;
int j=st[tail-1];
int l=can[tail-1],r=mi;
while(l+1!=r)
if(you(j,mid)) r=mid;
else l=mid;
return r;
}
int main()
{
//freopen("1.in","r",stdin);freopen("1.out","w",stdout);
int n,m;
cin>>n>>m;
rep(i,1,n)
{
int s;
scanf("%d%d",&s,&l[i].v);
l[i].next=t[s];
t[s]=i;
}
dp[0][0]=0;
for(s=1;s<=300;++s)
{
if(!t[s])
{
memcpy(dp[s],dp[s-1],sizeof(dp[s]));
continue;
}
top=0;
for(int i=t[s];i;i=l[i].next) q[++top]=l[i].v;
sort(q+1,q+top+1,greater<int>() );
rep(i,1,top)val[i]=val[i-1]+q[i];
rep(i,top+1,m)val[i]=0;
for(d=0;d<s;++d)
{
mi=(m-d)/s;
head=1;tail=0;
for(i=0;i<=mi;++i)
{
while(head<tail&&can[head+1]<=i)++head;
if(can[head]<i)can[head]=i;
while(head<=tail&&you(st[tail],can[tail]))--tail;
if(head>tail)
{
st[++tail]=i;can[tail]=i;
}
else
if(you(st[tail],mi))
{
st[++tail]=i;can[tail]=erfen();
}
dp[s][i*s+d]=get(st[head],i);
}
}
}
rep(i,1,m)printf("%lld ",dp[300][i]);
}
第二种:n^3-> n^2
(1)区间包含的单调性:如果对于i≤i'<j≤j',有w(i',j)≤w(i,j'),那么说明w具有区间包含的单调性。(可以形象理解为如果小区间包含于大区间中,那么小区间的w值不超过大区间的w值)
(2)四边形不等式:如果对于i≤i'<j≤j',有w(i,j)+w(i',j')≤w(i',j)+w(i,j'),我们称函数w满足四边形不等式。(可以形象理解为两个交错区间的w的和不超过小区间与大区间的w的和)
下面给出两个定理
定理一:如果上述的w函数同时满足区间包含单调性和四边形不等式性质,那么函数m也满足四边形不等式性质。
我们再定义s(i,j)表示m(i,j)取得最优值时对应的下标(即i≤k≤j时,k处的w值最大,则s(i,j)=k)。此时有如下定理
定理二:假如m(i,j)满足四边形不等式,那么s(i,j)单调,即s(i,j)≤s(i,j+1)≤s(i+1,j+1)。
例题: poj1160
code
#include<stdio.h>
#include<string.h>
#define MAXD 310
#define MAXP 40
#define INF 0x3f3f3f3f
int N, P, f[MAXD][MAXD], A[MAXD], a[MAXD], K[MAXD][MAXD];
void init()
{
int i, j, k;
A[0] = 0;
for(i = 1; i <= N; i ++)
{
scanf("%d", &a[i]);
A[i] = A[i - 1] + a[i];
}
}
int getw(int x, int y)
{
int t = (x + y) / 2;
return A[y] - A[t] - (y - t) * a[t] + (t - x) * a[t] - (A[t - 1] - A[x - 1]);
}
void solve()
{
int i, j, k, p, t;
for(i = 0; i <= N; i ++)
{
f[i][i] = 0;
K[i][i] = i;
}
for(p = 1; p <= N - P; p ++)
{
for(i = 0; (j = i + p) <= N; i ++)
f[i][j] = INF;
for(i = 1; (j = i + p) <= N; i ++)
{
for(k = K[i][j - 1]; k <= K[i + 1][j]; k ++)
if((t = f[i - 1][k - 1] + getw(k, j)) < f[i][j])
{
f[i][j] = t;
K[i][j] = k;
}
}
}
printf("%d\n", f[P][N]);
}
int main()
{
while(scanf("%d%d", &N, &P) == 2)
{
init();
solve();
}
return 0;
}
本来无法理解三重循环为何n^2,后来模拟了一遍,
从小区间推向大区间即从dp方阵的每条对角线(好吧不是对角线 就是表达那个意思)从中间向两边转移,
,每条对角线的转移时O(n)的,然后有O(n)条对角线,于是就O(n^2)了,太神了%%%。