区间DP(学习笔记)
P1880 [NOI1995] 石子合并
特点:
\(1.\)合并: 他可以有两个或多个区间合并而成进行求解,反之亦然
\(2.\)求解: 对于求每个区间的最优解,可以来求其子区间的最优解来合并转移,如一区间的最优解是其左右区间合并的最优解,那就合并左右子区间的最优解来求该区间的最优解
实现:
\(1.\)找到题目的动态转移方程,区间\(DP\)多为该形式:
令\(f_{i,j}\)表示\(i\)到\(j\)的最小值则有:
\[$f_{i,j}=min(f_{i,j},f_{i,k}+f_{k+1,j}+cost)$ \]这是通用基础式,不是通式,具体情况要根据题目来具体分析
\(2.\)根据题目要求来进行状态转移的实现
例题实践:
阅读题目,发现这道题围成的是一个环,那我们先从一条链开始讨论:
\(1.\)找到动态转移方程: 令\(f[i][j]\)为把\(i\)区间\(j\)的所有石子合并起来的最大得分,则有:
\[$f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+\sum_{t=i}^{j}a[t])$ \]\(a[i]\)表示第\(i\)堆石子的石子数量
\(2.\)考虑优化:\(\sum_{t=i}^{j}a[i]\)可以用前缀和\(sum[i]\)来维护,则动态转移变为:
\[$f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1])$ \]\(3.\)实现转移: 因为转移的前提是已经知道其子区间\(f[i][k]\)和\(f[k+1][j]\)的值,因此我们定义一个\(len\)表使当前区间 \(i,j\)的长度,在循环枚举\(i\),用\(len\) 算出\(j=i+len-1\)。最后枚举\(k\)进行动态转移,复杂度为\(n^3\)
考虑是一个环,这我们可以将整个序列复制一份到末尾,使其成为\(2 \times n\)堆石子,使第\(n+i\)堆与敌\(i\)堆相等,则其也成为了类似一个环,在经行区间\(dp\)即可
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=200;
int maxn[N][N],minn[N][N];
int a[N],sum[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
for(int i=1;i<=(n<<1);i++){
sum[i]=sum[i-1]+a[i];
maxn[i][i]=0;
minn[i][i]=0;
}
for(int len=2;len<=n;len++){
for(int i=1;i<=2*n-len+1;i++){
int j=i+len-1;
maxn[i][j]=INT_MIN;
minn[i][j]=INT_MAX;
for(int k=i;k<j;k++){
maxn[i][j]=max(maxn[i][j],maxn[i][k]+maxn[k+1][j]);
minn[i][j]=min(minn[i][j],minn[i][k]+minn[k+1][j]);
}
minn[i][j]+=sum[j]-sum[i-1];
maxn[i][j]+=sum[j]-sum[i-1];
}
}
int mn=INT_MAX,mx=INT_MIN;
for(int i=1;i<=n;i++){
mn=min(mn,minn[i][i+n-1]);
mx=max(mx,maxn[i][i+n-1]);
}
cout<<mn<<"\n"<<mx;
}
练习:
\(1.P1220\) 关路灯 题解
\(2.P3205 [HNOI2010]\) 合唱队 题解
\(3.P4767 [IOI2000]\) 邮局 加强版 题解
本文作者:XichenOC
本文链接:https://www.cnblogs.com/XichenOC/p/18682391
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步