区间DP(一)
还是刷了一些题,总结一下吧。
枚举顺序
AT_arc108_e
用
拆开来有:
有一种妙的枚举顺序,左端点从大到小,右端点从小到大,这样一来就可以用树状数组来维护了。统计答案可以用哨兵元素,复杂度
可以推广,也就是说我们的枚举顺序应该服务于我们的转移顺序。另一个经典的例子是 邮局,根据四边形不等式,
P2339
一道应该挺经典的题目。从大区间往小区间转移即可,复杂度平方。
状态设计
这是这几道题做下来的最深的感悟。总结起来就是说,如果一个状态发现无法正确转移,那就说明缺点什么;至于缺点什么,可以从决策过程来思考。在区间 DP 中,缺的这个东西常常是值。然后以几道题为例子说一下。
一个结论是,如果确定了一个题是区间 DP,那么就可以通过数据规模来猜。
P3607
首先有个思路是把一个子序列翻转等同于选出一些位置对,当然了这些位置对需要形成一个套娃的关系,并交换对应位置对上的数。这很好理解。既然是套娃的关系,那么似乎就可以用区间 DP 来解决了。
朴素地用
read(m);
for(int i=1;i<=m;i++){
read(a[i]);
for(int j=1;j<=a[i];j++)for(int k=a[i];k<N;k++)f[i][i][j][k]=1;
}
for(int len=1;len<m;len++)for(int l=1;l+len<=m;l++){
int r=l+len;
for(int y=1;y<N;y++)for(int x=y;x;x--){
check(f[l][r][x][y],f[l+1][r][x][y]+(a[l]==x));
check(f[l][r][x][y],f[l][r-1][x][y]+(a[r]==y));
check(f[l][r][x][y],f[l+1][r-1][x][y]+(a[l]==y)+(a[r]==x));
check(f[l][r][x][y],f[l][r][x+1][y]);
check(f[l][r][x][y],f[l][r][x][y-1]);
}
}
printf("%d\n",f[1][m][1][N-1]);
P4766
也是一个非常巧妙的题目。首先粗略地用
void solve(){
read(m);n=0;
for(int i=1;i<=m;i++){
read(a[i].l);read(a[i].r);read(a[i].d);
b[++n]=a[i].l,b[++n]=a[i].r;
}
sort(b+1,b+n+1);n=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=m;i++){
a[i].l=lower_bound(b+1,b+n+1,a[i].l)-b;
a[i].r=lower_bound(b+1,b+n+1,a[i].r)-b;
}
for(int len=0;len<n;len++){
for(int l=1;l+len<=n;l++){
int r=l+len,pl=0;f[l][r]=inf;
for(int i=1;i<=m;i++)
if(a[i].l>=l&&a[i].r<=r&&a[i].d>a[pl].d)pl=i;
if(pl==0){f[l][r]=0;continue;}
for(int i=a[pl].l;i<=a[pl].r;i++)
f[l][r]=min(f[l][r],f[l][i-1]+f[i+1][r]+a[pl].d);
}
}
printf("%d\n",f[1][n]);return;
}
P5336
也是一道非常好的区间 DP 题目。会发现直接做非常不好做,所以还是按照上面的那种思路,从决策的角度来设计状态。考虑对于一个区间,肯定是未卜先知地选定一个值域区间
signed main(){
read(m);read(aa);read(bb);
for(int i=1;i<=m;i++)read(a[i]),b[i]=a[i];
sort(b+1,b+m+1);n=unique(b+1,b+m+1)-b-1;
memset(f,0x3f,sizeof(f));memset(g,0x3f,sizeof(g));
for(int i=1;i<=m;i++){
a[i]=lower_bound(b+1,b+n+1,a[i])-b;f[i][i]=aa;
for(int ll=1;ll<=a[i];ll++)for(int rr=a[i];rr<=n;rr++)g[i][i][ll][rr]=0;
}
for(int len=1;len<m;len++)for(int l=1;l+len<=m;l++){
int r=l+len,nowData=inf;
for(int sa=1;sa<=n;sa++)for(int sb=sa;sb<=n;sb++){
int nowVal=inf;
for(int i=l;i<r;i++)
check(nowVal,min(f[l][i],g[l][i][sa][sb])+min(f[i+1][r],g[i+1][r][sa][sb]));
g[l][r][sa][sb]=nowVal;
check(f[l][r],nowVal+(b[sb]-b[sa])*(b[sb]-b[sa])*bb+aa);
}
}
printf("%d\n",f[1][m]);return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】