<学习笔记> 四边形不等式

四边形不等式

对于任意的 l1l2r1r2,满足 w(l1,r1)+w(l2,r2)w(l1,r2)+w(l2,r1)
若等号恒成立,则称函数 w 为四边形恒等式。交叉小于包含。

  • 如何证明

若满足 w(l,r1)+w(l+1,r)w(l,r)+w(l+1,r1),则 w 满足四边形不等式。

  • 决策单调性

对于任意的 i1<i2 必然成立 opti1<opti2

  • 区间单调性

对于任意的 ll1r1r,都有 w(l1,r1)w(l,r)

对于一个 dp 转移方程:

(1)fi=min1jiw(j,i)

定理一#

w 满足四边形不等式,则以问题(1)满足决策单调性。

证明

可以用反证法证明。设 c<da=opt(d)<opt(c)=b,那么有 a<b<c<d。根据最优化条件,有 w(a,d)<w(b,d)w(b,c)<w(a,c),那么有 w(a,d)w(b,d)<w(a,c)w(b,d) 与四边形不等式矛盾。

例题:「POI2011」Lightning Conductor#

fi=minjiajij+ai

w(l,r)=rl 满足区间包含单调性和四边形不等式,h(x)=x 是下凸函数,且 aj+ai 满足四边形恒等式,用到下文性质三,所以 fi 满足决策单调性,可以分治求解。

code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int inf=-(1<<20);
double h[N];
double ans[N];
double sqr[N],ans1[N];
void solve(int l,int r,int ls,int rs){
    if(l>r) return;
    if(l==r){
        rs=min(rs,l);
        for(int i=ls;i<=rs;i++){
            double sum=h[l]-h[i]-sqr[l-i];
            ans[l]=min(ans[l],sum);
        }
        return;
    }
    int mid=(l+r)/2;
    int p=min(rs,mid);
    int pos=0;
    for(int i=ls;i<=p;i++){
        double sum=h[mid]-h[i]-sqr[mid-i];
        if(ans[mid]>sum){
            ans[mid]=sum;
            pos=i;
        }
    }
    solve(l,mid,ls,pos);
    solve(mid+1,r,pos,rs);
    return;
}
void solve1(int l,int r,int ls,int rs){
    if(l>r) return;
    if(l==r){
        rs=min(rs,l);
        for(int i=ls;i<=rs;i++){
            double sum=h[l]-h[i]-sqr[l-i];
            ans1[l]=min(ans1[l],sum);
        }
        return;
    }
    int mid=(l+r)/2;
    int p=min(rs,mid);
    int pos=0;
    for(int i=ls;i<=p;i++){
        double sum=h[mid]-h[i]-sqr[mid-i];
        if(ans1[mid]>sum){
            ans1[mid]=sum;
            pos=i;
        }
    }
    solve1(l,mid,ls,pos);
    solve1(mid+1,r,pos,rs);
    return;
}
signed main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) sqr[i]=sqrt(i);
    for(int i=1;i<=n;i++) scanf("%lf",&h[i]),ans[i]=ans1[i]=-inf;
    solve(1,n,1,n);
    for(int i=1;i<=n/2;i++){
        swap(h[i],h[n-i+1]);
    }
    solve1(1,n,1,n);
    for(int i=1;i<=n;i++){
        int cnt=floor(min(ans[i],ans1[n-i+1]));
        printf("%d\n",-cnt);
    }
}

区间类 2D/1D DP#

形如

(2)f(j,i)=minjk<if(j,k)+f(k+1,i)+w(j,i)(1j<in)

状态数为 n2,单次转移为 n,类似于区间合并。

引理一#

w 满足区间包含单调性和四边形不等式,则状态 f(j,i) 满足四边形不等式。

证明

定理二#

w 满足区间包含单调性和四边形不等式,问题(3)中的最优决策 opt(i,j) 满足:

opt(j,i1)opt(j,i)opt(j+1,i).(j+1<i)

证明

当固定 j 时,构成的函数满足四边形不等式,根据定理一得 opt(j,i)<opt(j,i+1)。另一侧同理。

所以每次记下最优决策点,转移时只在 opt(j,i1) opt(j+1,i) 枚举就行,枚举总量为 O(n),所以复杂度 O(n2)

1D/1D DP#

  • 问题

考虑将某个区间拆分成若干个子区间的问题,求代价最小。

那么将列出如下转移:

(3)f(i)=min1jif(j1)+w(j,i)(1in)

状态数 O(n),转移复杂度 O(n),考虑四边形不等式优化,

w(i,j) 满足四边形不等式,那么 fj+w(i,j) 也满足四边形不等式(因为第一项不包含 i,j 交叉项),因此这个具有决策单调性。

但是转移必须从前到后,那么只限制了下界,没有上界,所以不可以用分治,那么就需要用二分队列。

如果在此问题上加上 m 个数限制,那么又有如下转移:

(4)f(k,i)=min1jif(k1,j1)+w(j,i)(1km, 1in)

定理三#

w 满足四边形不等式,那么问题(4) 中的问题满足 opt(k1,i)opt(k,i)opt(k,i+1)

假如 w(j,i) 满足四边形不等式,那么 f(k1,j1)+w(j,i) 也满足四边形不等式。那么这个就可以进行 k 次分治,将其优化为 O(nmlogn)

求解方法#

分治#

(l,r,ls,rs) 表示 fl r 的决策点在 (ls rs) 之间。我们考虑 mid 的决策点 pos,可以暴力求出。然后左右区间的决策点就有了新上下界,然后递归到这两个区间求解,复杂度 O(nlogn)

二分队列#

对于当前状态依赖于前面的状态的,例如问题 (3),我们就不可以分治,只能二分队列。

因为决策是单调的,那么每个决策点 i 所处理的问题 j 也构成一个区间,设三元组 (i,li,ri) 表示 i 处理的区间为 li rival(i,j) 表示从 i 转移到 jfj 的值,算法如下:

  • 初始化: 将最初的决策点压入队列,比如将 (0,1,n) 压入队列,表示它对于所有点目前它是最优的。

  • 计算: 每次从队首取出第一个满足 ljirjj, 有 j 更新 i。顺便将所有 rji 的出队。

  • 入队:

    • 设队尾为 j,如果 val(i,lj)val(j,lj) 则弹出队尾。持续到队列为空或不满足。

    • 如果队列已空,那么直接将决策 (i,i+1,n) 压入。

    • 若不为空,我们还考虑队尾决策 j,此时对于 lj 决策 j 优于 i

      • 如果 val(i,rj)val(j,rj),那么直接加入 (i,rj+1,n)

      • 否则,我们需要找到那个分界点,直接二分出来最小的使 val(i,p)val(j,p) 的点,将队尾 rj 改为 p1,然后加入 (i,p,n)

代码可以看例题 P1912。

满足四边形不等式的函数类#

性质一: 若函数 w1(i,j)w2(i,j) 满足四边形不等式(或区间单调性),那么对于任意的 c10,c20,函数 c1w1(i,j)+c2w2(i,j) 满足四边形不等式(或区间单调性)。

性质二: 若存在函数 f(x)g(x)w(i,j)=f(i)g(j),则 w(i,j) 满足四边形恒等式。当 fg 单调增加时,则 w 还满足区间包含单调性。

性质三:f(x) 是一个单调增加的下凸函数,若函数 w(i,j) 满足四边形不等式和区间包含单调性,则复合函数 f(w(i,j)) 也满足四边形不等式和区间包含单调性。

性质四:f(x) 是一个下凸函数,若函数 w(i,j) 满足四边形不等式和区间包含单调性,则复合函数 f(w(i,j)) 也满足四边形不等式。

对于取 max 的情况,其实就是讲比较取反证明。毕竟全靠猜

例题#

P1912 [NOI2009] 诗人小G#

fi=minj<i(fj+wj+1,i)(1in)

wj,i=|sisjL|p

如何证明 w 满足四边形不等式

外层外层函数 y=|x|p(x>0) 为下凸函数;内层函数 w1(i,j)=lenilenjL 满足四边形不等式且满足区间包含单调性,所以 w 满足四边形不等式。

这种形式只能用二分队列(反正我只会这个),注意会爆 long long ,所以用 long double 存。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define LD long double
using namespace std;
const int N=1e5+10;
const int inf=1e18;
char a[N][40];
LD mgml(LD x,int p){
    LD ans=1;
    while(p){
        if(p&1) ans=ans*x;
        x=x*x;
        p>>=1;
    }
    return ans;
}
int n,L,p;
int s[N];
LD val(int x,int y){
    return mgml(abs(s[y]-s[x]-L+y-x-1),p);
}
int r[N],l[N];
int q[N],head,tail;
LD f[N];
int las[N];
int st[N],top=0;
signed main(){
    int T;
    scanf("%lld",&T);
    while(T--){
        memset(f,127,sizeof(f));
        memset(las,0,sizeof(las));
        memset(st,0,sizeof(st));
        scanf("%lld%lld%lld",&n,&L,&p);
        s[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%s",a[i]+1);
            s[i]=s[i-1]+strlen(a[i]+1);
        }
        head=1,tail=0;
        f[0]=0;
        q[++tail]=0;
        l[0]=1,r[0]=n;
        for(int i=1;i<=n;i++){
            while(head<=tail && r[q[head]]<i) head++;
            las[i]=q[head];
            f[i]=f[q[head]]+val(q[head],i);
            while(head<=tail &&f[i]+val(i,l[q[tail]]) < f[q[tail]]+val(q[tail],l[q[tail]])) tail--;
            if(head>tail){
                q[++tail]=i;
                l[i]=i+1,r[i]=n;
            }           
            else if(f[i]+val(i,r[q[tail]]) > f[q[tail]]+val(q[tail],r[q[tail]])){
                if(r[q[tail]]<n){
                    l[i]=r[q[tail]]+1;
                    r[i]=n;
                    q[++tail]=i;
                }
            }
            else{
                int ls=l[q[tail]],rs=r[q[tail]];
                int pos=0;
                while(ls<=rs){
                    int mid=(ls+rs)/2;
                    if(f[i]+val(i,mid) <= f[q[tail]]+val(q[tail],mid)){
                        pos=mid;
                        rs=mid-1;
                    }
                    else ls=mid+1;
                }
                r[q[tail]]=pos-1;
                if(l[q[tail]]>l[q[tail]]) tail--;
                q[++tail]=i;
                l[i]=pos;
                r[i]=n;
            }
        }
        if(f[n]>inf) printf("Too hard to arrange\n");
        else{
            printf("%.0Lf\n",f[n]);
            int tmp=n;
            top=0;
            while(tmp){
                st[++top]=tmp;
                tmp=las[tmp];
            }
            for(int i=top;i>=1;i--){
                for(int j=st[i+1]+1;j<=st[i];j++){
                    cout<<a[j]+1;
                    if(j!=st[i]) cout<<" ";
                }
                printf("\n");
            }
        }
        printf("--------------------\n");
    }
}

锯木厂选址#

转移形式与问题 (1) 类似,所以直接分治求解就可以。

邮局#

fi,j=minj<ifi1,j+w(i,j)

如果 w(i,j) 满足四边形不等式,那么 fi,j 也满足四边形不等式。

所以可以对每一层进行分治解决,复杂度 O(nmlogn)

现在我们考虑如何证明 w(i,j) 满足四边形不等式:

为了证明其满足,我们只需要证明 w(l,r)+w(l+1,r+1)w(l,r+1)+w(l+1,r)

我们考虑一个处于 (l,r+1) 范围内的村庄 i,先不考虑位于 l,l+1,r,r+1,那么只位于 l+1 r 之间。

分情况讨论,四个式子分别选 (l/r),(l+1/r+1),(l/r+1),(l+1/r) 是左右两侧的值,可以发现排除不合法的,剩下的均满足 w(l,r)+w(l+1,r+1)w(l,r+1)+w(l+1,r),对于端点处的,同理,得证。

Yet Another Minimization Problem / [CmdOI2019] 任务分配问题 / The Bakery #

转移与 (4) 类似,发现贡献满足四边形恒等式,对于计算贡献可以维护一个全局指针,发现总共扫 nlogn 次,复杂度 nlogn

code
// Yet Another Minimization Problem 
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=(1ll<<50);
const int N=1e5+10;
int f[25][N];
int sum[N],a[N];
int n,k;
struct asd{
    int px=1,py=0,w=0;
    int ask(int l,int r){
        while(py<r){
            py++;
            if(sum[a[py]]) w-=(sum[a[py]]*(sum[a[py]]-1)/2);
            sum[a[py]]++;
            w+=(sum[a[py]]*(sum[a[py]]-1)/2);
        }
        while(px>l){
            px--;
            if(sum[a[px]]) w-=(sum[a[px]]*(sum[a[px]]-1)/2);
            sum[a[px]]++;
            w+=(sum[a[px]]*(sum[a[px]]-1)/2);
        }
        while(py>r){
            w-=(sum[a[py]]*(sum[a[py]]-1)/2);
            sum[a[py]]--;
            if(sum[a[py]]) w+=(sum[a[py]]*(sum[a[py]]-1)/2);
            py--;
        }
        while(px<l){
            w-=(sum[a[px]]*(sum[a[px]]-1)/2);
            sum[a[px]]--;
            if(sum[a[px]]) w+=(sum[a[px]]*(sum[a[px]]-1)/2);
            px++;
        }
        return w;
    }
}T;
void solve(int p,int l,int r,int ls,int rs){
    if(l==r){
        int mn=min(l-1,rs);
        for(int i=ls;i<=mn;i++){
            f[p][l]=min(f[p][l],f[p-1][i]+T.ask(i+1,l));
        }
        return;
    }
    int mid=(l+r)/2;
    int mn=min(mid-1,rs);
    int pos=0;
    for(int i=ls;i<=mn;i++){
        int cnt=f[p-1][i]+T.ask(i+1,mid);
        if(f[p][mid]>cnt && mid>i){
            f[p][mid]=cnt;
            pos=i;
        }
    }
    solve(p,l,mid,ls,pos);
    solve(p,mid+1,r,pos,rs);
}
signed main(){
    scanf("%lld%lld",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    T.ask(1,n);
    memset(f,0x7f,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=k;i++) solve(i,1,n,0,n);
    printf("%lld",f[k][n]);
}

参考资料#

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17913146.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu