四边形不等式
写的有点答辩了。
最简单的一种:
2D1D的状态转移方程:
当
-
区间包含单调性:
,那么 对于区间包含关系具有单调性。 -
四边形不等式:
(交叉小于包含),则 满足四边形不等式。等号恒成立称 满足 四边形恒等式。
- 若对于定义域上的任意整数
,有 ,则 满足四边形不等式。
对于
对于
相加得到
即对于任意
同理对于任意
- 若
满足区间包含单调性和四边形不等式,则 满足四边形不等式。
太长了不放,感性理解。
- 若状态
满足四边形不等式,记 为最优决策点,则
记
因为
两式相加可得
即
所以
因为
两式相加可得
即
所以
综上可得
得到决策单调性就可以进行DP了。
当计算
长这个样子。
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; } }
写了没保存。
数轴上
先将
设
那么
其中
显然这个点应该在区间的中位数处,可以暴力做。
复杂度为
考虑一次转移能不能做到
但是复杂度仍为
想一下
由递推式得
两式相减有
由于坐标递增得上式
所以
故
时间复杂度
#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
序列
正着做一次,翻转序列后再做一次就可以消掉绝对值。
设
令
令
这个式子是
那么
本题求
定义
对于
因为
与第一个不等式相加
即
求出
根据决策单调性,可以找到一个
将该位置及其后面的部分都变为
建立一个队列代替
对于每个
时间复杂度
这题把符号取反就好。
#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模板。
试最小化总代价并给出方案。多测。
-
若答案大于
输出Too hard to arrange
. -
极限数据有
, , , . -
所有
都 .
默认
这个东西是高次的所以斜优不了。
若证明
设
由于
拆一下绝对值。
:肉眼看比较显然。
求导?
容易发现
就是
同样
不知道为什么要这么严谨证。
把流程再放一遍。还是不太会。
建立一个队列代替
对于每个
时间复杂度
答案大于
贺了一个长得比较像斜优的题解。
#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
将序列
试最小化总代价。
记
证明
怎么用比较优秀的复杂度求出
但是使用单调队列无法很好地保证指针移动的次数。这个时候就有必要用到分治优化了。
即
由于决策单调性它的时间复杂度是
时间复杂度
#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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步