四边形不等式

总结一下最近几天对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)了,太神了%%%。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值