<学习笔记> 四边形不等式
四边形不等式
对于任意的
,满足 。
若等号恒成立,则称函数为四边形恒等式。交叉小于包含。
- 如何证明
若满足
,则 满足四边形不等式。
- 决策单调性
对于任意的
必然成立 。
- 区间单调性
对于任意的
,都有 。
对于一个
定理一#
若
满足四边形不等式,则以问题(1)满足决策单调性。
证明
可以用反证法证明。设
例题:「POI2011」Lightning Conductor#
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#
形如
状态数为
引理一#
若
满足区间包含单调性和四边形不等式,则状态 满足四边形不等式。
证明
定理二#
若
满足区间包含单调性和四边形不等式,问题(3)中的最优决策 满足:
证明
当固定
所以每次记下最优决策点,转移时只在
1D/1D DP#
- 问题
考虑将某个区间拆分成若干个子区间的问题,求代价最小。
那么将列出如下转移:
状态数
若
但是转移必须从前到后,那么只限制了下界,没有上界,所以不可以用分治,那么就需要用二分队列。
如果在此问题上加上
定理三#
若
满足四边形不等式,那么问题(4) 中的问题满足 。
假如
求解方法#
分治#
设
二分队列#
对于当前状态依赖于前面的状态的,例如问题
因为决策是单调的,那么每个决策点
-
初始化: 将最初的决策点压入队列,比如将 (0,1,n) 压入队列,表示它对于所有点目前它是最优的。
-
计算: 每次从队首取出第一个满足
的 , 有 更新 。顺便将所有 的出队。 -
入队:
-
设队尾为
,如果 则弹出队尾。持续到队列为空或不满足。 -
如果队列已空,那么直接将决策
压入。 -
若不为空,我们还考虑队尾决策
,此时对于 决策 优于 。-
如果
,那么直接加入 。 -
否则,我们需要找到那个分界点,直接二分出来最小的使
的点,将队尾 改为 ,然后加入 。
-
-
代码可以看例题 P1912。
满足四边形不等式的函数类#
性质一: 若函数
性质二: 若存在函数
性质三: 设
性质四: 设
对于取 毕竟全靠猜
例题#
P1912 [NOI2009] 诗人小G#
如何证明
外层外层函数
这种形式只能用二分队列(反正我只会这个),注意会爆 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");
}
}
锯木厂选址#
转移形式与问题
邮局#
如果
所以可以对每一层进行分治解决,复杂度
现在我们考虑如何证明
为了证明其满足,我们只需要证明
我们考虑一个处于
分情况讨论,四个式子分别选
Yet Another Minimization Problem / [CmdOI2019] 任务分配问题 / The Bakery #
转移与
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 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效