四边形不等式

写的有点答辩了。

四边形不等式优化

最简单的一种:

2D1D的状态转移方程:

fl,r=mink=lr1{fl,k+fk+1,r}+w(l,r)

w(l,r) 满足一些性质时能利用决策单调性优化。

  • 区间包含单调性 llrr,w(l,r)w(l,r) ,那么 w 对于区间包含关系具有单调性。

  • 四边形不等式 l1l2r1r2,w(l1,r1)+w(l2,r2)w(l1,r2)+w(l2,r1) (交叉小于包含),则 w 满足四边形不等式。等号恒成立称 w 满足 四边形恒等式


  • 若对于定义域上的任意整数 a<b,有 w(a,b+1)+w(a+1,b)w(a,b)+w(a+1,b+1),则 w 满足四边形不等式。

对于 a<c,有 w(a,c+1)+w(a+1,c)w(a,c)+w(a+1,c+1)

对于 a+1<c,有 w(a+1,c+1)+w(a+2,c)w(a+1,c)+w(a+2,c+1)

相加得到 w(a,c+1)+w(a+2,c)w(a,c)+w(a+2,c+1)

即对于任意 abc,有 w(a,c+1)+w(b,c)w(a,c)+w(b,c+1) .

同理对于任意 abcd,有 w(a,d)+w(b,c)w(a,c)+w(b,d) .


  • w(l,r) 满足区间包含单调性和四边形不等式,则 f(l,r) 满足四边形不等式。

太长了不放,感性理解。


  • 若状态 f 满足四边形不等式,记 ml,r=min{k:fl,r=gk,l,r} 为最优决策点,则

ml,r1ml,rml+1,r (l+1<r)

u=ml,r,k1=ml,r1,k2=ml+1,r ,即证明 k1uk2 .

1.k1>u ,则 u+1k1+1r1r ,有

fu+1,r1+fk1+1,rfu+1,r+fk1+1,r1

因为 ufl,r 的最优决策点:

fl,u+fu+1,rfl,k1+fk1+1,r

两式相加可得

fl,u+fu+1,r1fl,k1+fk1+1,r1

gu,l,r1gk1,l,r1 ,那么 u=ml,r1 ,与 k1>u 矛盾。

所以 k1u .

2.u>k2 ,则 ll+1k2u ,有

fl,k2+fl+1,ufl,u+fl+1,k2

因为 k2fl+1,r 的最优决策点:

fl+1,k2+fk2+1,rfl+1,u+fu+1,r

两式相加可得

fl,k2+fk2+1,rfl,u+fu+1,r

gk2,l,rgu,l,r ,那么 k2=ml,r ,与 u>k2 矛盾。

所以 uk2 .

综上可得 k1uk2 ,即 ml,r1ml,rml+1,r .


得到决策单调性就可以进行DP了。

当计算 fl,r 时,将其最优决策点 ml,r 记录下来,那么对决策点的枚举总量为

1lrnml+1,rml,r1=i=1nmi,nm1,in2

长这个样子。

for(int len=2;len<=n;len++)
for(int l=1,r=len;r<=n;l++,r++){
f[l][r]=inf;
for(int k=m[l][r-1];k<=m[l+1][r];k++)
if(f[l][r]>f[l][k]+f[k+1][r]+w(l,r)){
f[l][r]=f[l][k]+f[k+1][r]+w(l,r);
m[l][r]=k;
}
}

写了没保存。

P4767 [IOI2000]邮局

数轴上 n 个点 {a} ,放 m 个点,最小化

i=1nminj=1m|aibj|

n3000,m300 .

先将 a 升序排序。

f(i,j) 表示考虑前 i 个点,放置 j 个点的最小值。

那么

f(i,j)=mink=0i1f(k,j1)+w(k+1,i)

其中 w(l,r) 指在这段区间放一个点的最小距离和。

显然这个点应该在区间的中位数处,可以暴力做。

复杂度为 O(n3m) .

考虑一次转移能不能做到 O(1) ,能发现 w 是可以 O(n2) 预处理出来的,有

w(l,r)=w(l,r1)+aral+r2

但是复杂度仍为 O(n2m) .

想一下 w 是否满足四边形不等式。

由递推式得

w(l,r+1)w(l,r)=ar+1al+r+12

w(l+1,r+1)w(l+1,r)=ar+1al+r+22

两式相减有

w(l,r+1)+w(l+1,r)w(l,r)w(l+1,r+1)=al+r+12al+r+22

由于坐标递增得上式 0 .

所以

w(l,r)+w(l+1,r+1)w(l,r+1)+w(l+1,r)

f 满足四边形不等式,于是可以利用决策单调性进行优化。

时间复杂度 O(nm) .

#include<bits/stdc++.h>
#define N 3010
#define M 305
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,m,a[N];
int w[N][N],f[N][M],b[N][M];
void init(){
for(int l=1;l<=n;l++){
w[l][l]=0;
for(int r=l+1;r<=n;r++)
w[l][r]=w[l][r-1]+a[r]-a[(l+r)>>1];
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read();
sort(a+1,a+1+n);
memset(f,0x3f,sizeof(f));
init(),f[0][0]=0;
for(int j=1;j<=m;j++){
b[n+1][j]=n;
for(int i=n;i;i--){
for(int k=b[i][j-1];k<=b[i+1][j];k++)
if(f[i][j]>f[k][j-1]+w[k+1][i])
f[i][j]=f[k][j-1]+w[k+1][i],b[i][j]=k;
}
}
printf("%d\n",f[n][m]);
return 0;
}

P3515 [POI2011]Lightning Conductor

序列 {an},对于每个 i[1,n],求出最小的非负整数 p 满足 j[1,n]ajai+p|ij| .

n5×1050ai109 .

paj+|ij|ai

p=max{aj+|ij|}ai

正着做一次,翻转序列后再做一次就可以消掉绝对值。

f(i)=max{aj+ij}w(j,i)=ij .

l+1<r

w(l,r+1)+w(l+1,r)w(l,r)w(l+1,r+1)

=rl1+rl+12rl

t=rl

=(t+1t)(tt1)

这个式子是 <0 的。

那么 abcd,有 w(a,d)+w(b,c)w(a,c)+w(b,d) .

本题求 max,将符号取反,可以证明 f 满足决策单调性。

定义 Pii 的最优决策点。

对于 j[0,Pi1],有

f(Pi)+w(Pi,i)f(j)+w(j,i)

因为 w 满足四边形不等式,对于 i[i+1,n],有

w(j,i)+w(Pi,i)w(j,i)+w(Pi,i)

w(Pi,i)w(Pi,i)w(j,i)w(j,i)

与第一个不等式相加

f(Pi)+w(Pi,i)f(j)+w(j,i)

i 的最优决策点一定 Pif 具有决策单调性。

求出 f(i) 是考虑其可以作为哪些 f(i) 的最优决策。

根据决策单调性,可以找到一个 pos,这之前的决策都比 i 优,后面的都比 i 差。

将该位置及其后面的部分都变为 i,此时它们的最优决策点均为 i .

建立一个队列代替 P,保存三元组 (j,l,r),表示 P(l~r) 都是 j .

对于每个 i,执行如下操作:

1. 设队头为 (j0,l0,r0),若 r0=i1 则删除队头,因为 f(i) 之前的值都已求出,否则令 l0=i .

2. 取队头的最优决策 j 转移算出 f(i) .

3. 插入新决策 i

(1). 取出队尾 (jt,lt,rt) .

(2). 若对于 f(lt)ijt 更优,即 f(i)+w(i,lt)f(j)+w(jt,lt),记 pos=lt,删除队尾返回到 (1) .

(3). 若对于 f(rt)ijt 更优,去往 (5) .

(4). 否则,在 [lt,rt] 上二分出 pos,在此之前决策优于 i,后面劣于 i,将 [lt,rt] 改为 [lt,pos1],去往 (5) .

(5). 将三元组 (i,pos,n) 插入队尾。

时间复杂度 O(nlogn) .

这题把符号取反就好。

#include<bits/stdc++.h>
#define db double
#define N 500010
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,a[N],h,t;
db f[N],sqr[N];
struct node{
int l,r,p;
}q[N];
db w(int j,int i){
return db(a[j])+sqr[i-j];
}
int find(int t,int x){
int pos=q[t].r+1,l=q[t].l,r=q[t].r,mid;
while(l<=r){
mid=(l+r)>>1;
if(w(q[t].p,mid)<=w(x,mid))pos=mid,r=mid-1;
else l=mid+1;
}
return pos;
}
void insert(int i){
q[t].l=max(q[t].l,i);
while(h<=t&&w(i,q[t].l)>=w(q[t].p,q[t].l))t--;
if(h>t)q[++t]=(node){i,n,i};
else{
int pos=find(t,i);
if(pos>n)return;
q[t].r=pos-1;
q[++t]=(node){pos,n,i};
}
}
void work(){
h=1,t=0;
for(int i=1;i<=n;i++){
insert(i);
if(h<=t&&q[h].r<i)h++;
else q[h].l=i;
f[i]=max(f[i],w(q[h].p,i));
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
sqr[i]=sqrt(i);
}
work();
for(int i=1;i<=n/2;i++)
swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
work();
for(int i=n;i;i--)
printf("%d\n",(int)ceil(f[i])-a[i]);
return 0;
}

没总结出来比较干练的dp模板。

P1912 [NOI2009] 诗人小G

n 个字符串,长度分别为 ai,将它们按顺序划分成若干段,一段区间 [l,r] 的代价是

|(al+1+al+1+1++ar1+1+ar)L|P

试最小化总代价并给出方案。多测。

  • 若答案大于 1018 输出 Too hard to arrange.

  • 极限数据有 T5n105L3×106P10.

  • 所有 ai30.

默认 ai+1,记 sx=i=1xai.

dp 方程是

fi=minj=0i1{fj+|sisj(L+1)|P}

这个东西是高次的所以斜优不了。

若证明 w(j,i)=|sisj(L+1)|P 满足四边形不等式,就可以用单调队列解决。

w(a,b+1)+w(a+1,b)w(a,b)+w(a+1,b+1)

u=sisj(L+1),v=sisjaj(L+1)

|u+ai+1|P+|v|P|u|P+|v+ai+1|P

|v|P|v+ai+1|P|u|P|u+ai+1|P

由于 uv,即证 g(x)=|x|P|x+z|P(z[0,+)) 非严格单调递减。

拆一下绝对值。

  • x[0,):肉眼看比较显然。

求导?

g(x)=PxP1P(x+z)P1

容易发现 g(x)<0.

  • x(,0),P0 (mod 2)

就是 (x)P(x+z)P,和上面一样是递减的。

  • x[z,0),P1 (mod 2)

g(x)=xP(x+z)P

g(x)=PxP1P(x+z)P1

P 为奇数所以 g(x)0.

  • x(,z),P1 (mod 2)

g(x)=xP+(x+z)P

g(x)=PxP1+P(x+z)P1

同样 g(x)0.

不知道为什么要这么严谨证。

把流程再放一遍。还是不太会。

建立一个队列代替 P,保存三元组 (j,l,r),表示 P(l~r) 都是 j .

对于每个 i,执行如下操作:

1. 设队头为 (j0,l0,r0),若 r0=i1 则删除队头,因为 f(i) 之前的值都已求出,否则令 l0=i .

2. 取队头的最优决策 j 转移算出 f(i) .

3. 插入新决策 i

(1). 取出队尾 (jt,lt,rt) .

(2). 若对于 f(lt)ijt 更优,即 f(i)+w(i,lt)f(j)+w(jt,lt),记 pos=lt,删除队尾返回到 (1) .

(3). 若对于 f(rt)ijt 更优,去往 (5) .

(4). 否则,在 [lt,rt] 上二分出 pos,在此之前决策优于 i,后面劣于 i,将 [lt,rt] 改为 [lt,pos1],去往 (5) .

(5). 将三元组 (i,pos,n) 插入队尾。

时间复杂度 O(Tnlogn).

答案大于 1018 的时候会炸 long long,要开 long double 牺牲精度换取值域。

贺了一个长得比较像斜优的题解。

#include<bits/stdc++.h>
#define ll long long
#define lb long double
#define N 100010
#define R 32
using namespace std;
char st[N][R];
int T,n;
ll L,P,s[N];lb f[N];
int h,t,q[N],pre[N];
int stk[N],top;
lb qpow(lb k,ll b){
lb ret=1;
while(b){
if(b&1)ret*=k;
b>>=1,k*=k;
}
return ret;
}
lb calc(int j,int i){
return f[j]+qpow(abs(s[i]-s[j]-L),P);
}
int find(int t,int x){
if(calc(t,n)<calc(x,n))return n+1;
int pos=-1,l=x,r=n,mid;
while(l<=r){
mid=(l+r)>>1;
if(calc(x,mid)<=calc(t,mid))pos=mid,r=mid-1;
else l=mid+1;
}
return pos;
}
void work(){
h=1,t=0,q[++t]=0;
for(int i=1;i<=n;i++){
while(h<t&&find(q[h],q[h+1])<=i)h++;
pre[i]=q[h],f[i]=calc(q[h],i);
while(h<t&&find(q[t-1],q[t])>=find(q[t],i))t--;
q[++t]=i;
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%lld%lld",&n,&L,&P),L++;
for(int i=1;i<=n;i++){
scanf("%s",st[i]);
s[i]=s[i-1]+strlen(st[i])+1;
}
work();
if(f[n]>1e18){
printf("Too hard to arrange\n");
}
else{
printf("%lld\n",(ll)f[n]),top=0;
for(int tp=n;tp;tp=pre[tp])stk[++top]=tp;
stk[++top]=0;
for(int i=top;i>1;i--){
for(int j=stk[i]+1;j<stk[i-1];j++)
printf("%s ",st[j]);
printf("%s\n",st[stk[i-1]]);
}
}
printf("--------------------\n");
}
return 0;
}

Yet Another Minimization Problem

将序列 a 分成 m 段,每一段 [l,r] 的代价是其中相同元素的对数,即 lx<yr[ax=ay].

试最小化总代价。

2n1052mmin(n,20)1ain.

f(i,k) 为前 i 个划分成 k 段的最小代价。

f(i,k)=minj=0i1{f(j,k1)+w(j+1,i)}

证明 w 满足四边形不等式:

w(a,b+1)+w(a+1,b)w(a,b)w(a+1,b+1)

w(a,b+1)w(a,b)+w(a+1,b)w(a+1,b+1)

p=ab[ap=ab+1]p=a+1b[ap=ab+1]

=[aa=ab]0

怎么用比较优秀的复杂度求出 w 函数?可以像莫队一样左右移动指针做。

但是使用单调队列无法很好地保证指针移动的次数。这个时候就有必要用到分治优化了。

dp(l,r,bl,br) 表示已经确定 f[l] ~ f[r] 的最优决策点在 [bl,br],找到当前 mid 的最优决策 bm,然后两边继续递归,也就是 dp(l,mid1,bl,bm)dp(mid+1,r,bm,br) .

由于决策单调性它的时间复杂度是 O(nlogn) 的,不太会证。

时间复杂度 O(knlogn).

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 25
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,m,k,a[N];
int L,R,cnt[N];
ll sum,f[N][M];
int h,t,q[N];
void add(int x){
sum+=(cnt[x]++);
}
void del(int x){
sum-=(--cnt[x]);
}
ll w(int l,int r){
while(L>l)add(a[--L]);
while(R<r)add(a[++R]);
while(L<l)del(a[L++]);
while(R>r)del(a[R--]);
return sum;
}
void dp(int l,int r,int bl,int br){
if(l>r)return;
int mid=(l+r)>>1,bm=bl;
ll minn=f[bm][k-1]+w(bm+1,mid);
for(int i=bl;i<=min(br,mid-1);i++){
if(minn>f[i][k-1]+w(i+1,mid))
bm=i,minn=f[i][k-1]+w(i+1,mid);
}
f[mid][k]=minn;
dp(l,mid-1,bl,bm),dp(mid+1,r,bm,br);
}
int main(){
n=read(),m=read();
L=1,R=n;
for(int i=1;i<=n;i++){
a[i]=read(),sum+=(cnt[a[i]]++);
f[i][1]=sum;
}
for(k=2;k<=m;k++)dp(1,n,1,n);
printf("%lld\n",f[n][m]);
return 0;
}

本文作者:SE の 摆烂窝

本文链接:https://www.cnblogs.com/SError0819/p/17609862.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   SError  阅读(30)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起