区间 dp 总结
区间 \(DP\)
定义:
区间类动态规划是线性动态规划的拓展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并有很大关系。
操作:
定义 \(f[i][j]\) 表示将下标位置 \(i\) 到 \(j\) 的所有元素合并能获得的价值的最大值。
\[f[i][j]=max(f[i][k]+f[k+1][j]+cost)
\]
特点:
合并: 两个或多个部分进行整合,当然可以反过来。
特征: 能将问题分解为两两合并的形式
求解: 对整个问题设置最优解,枚举合并点,将问题分解为左右两部分,最后合并两个部分的最优值,得到原问题的最优值。
例题:
懒狗题意:
字符串能不能折叠更短(原来太长了)
分析:
看字符串长度,不超过 \(100\) ,而且包括区间的折叠,也就是合并,那么用区间 \(dp\) 岂不是美哉?
我们定义 \(dp[i][j]\) 表示 \([i,j]\) 范围内,字符串能折叠成的最小形式。
当进行合并时,开始有 \(dp\) 方程:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
下方对 \([i,j]\) 范围进行判断:
枚举能重叠的长度,如果 不能整除区间总长度 \((l\%len!=0)\) ,或者 区间并不是能合并的 ,就跳过。
若成功,则有转移公式:
dp[i][j]=min(dp[i][j],dp[i][k]+2+m[l/len]);
其中,\(m\) 数组为 \(l/len\) 的值。
代码:
// P4302 [SCOI2003]字符串折叠
#include<bits/stdc++.h>
using namespace std;
const int N=105;
char s[N];
int n,m[N],dp[N][N];
bool check(char s[],int n,int len){
for(int i=len;i<n;i++)
if(s[i]!=s[i%len])return false;
return true;
}
int main(){
scanf("%s",s); n=strlen(s);
for(int i=1;i<=9;i++) m[i]=1; for(int i=10;i<=99;i++) m[i]=2; m[100]=3;
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=0;i<n;i++) dp[i][i]=1;
for(int l=2;l<=n;l++){
for(int i=0,j=i+l-1;j<n;i++,j++){
for(int k=i;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
for(int k=i;k<j;k++){
int len=k-i+1;
if(l%len!=0) continue;
if(check(s+i,l,len))//枚举长度
dp[i][j]=min(dp[i][j],dp[i][k]+2+m[l/len]);
}
}
}
printf("%d",dp[0][n-1]);
system("pause");
return 0;
}
P2470 [SCOI2007]压缩
题意:
算是上一题加强版,不过就是多了几个转移方程
分析:
设 \(dp[i][j][0]\) 表示表示 \([i-j]\) 的区间内没有 \(M\) 的情况
\(dp[i][j][1]\) 则表示i到j的区间内有M的情况
先看 \(dp[i][j][0]\),如果前半段与后半段相等,那么则有转移:
\[dp[i][j][0]=min(dp[i][j][0],dp[l][mid][0]+1)
\]
其中,\(mid\) 为区间中点。
然后像平常的区间 \(dp\) 一样,枚举中间点 \(k\)
\[dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j−k)
\]
再考虑 \(dp[i][j][1]\) 的情况,此时的k枚举的是M的位置
\[dp[l][r][1]=min(dp[l][r][1],min(dp[l][k][0],dp[l][k][1])+min(dp[k+1][r][0],dp[k+1][r][1])+1)
\]
最后输出 \(max(dp[1][n][0],dp[1][n][1])\) 即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=55;
char s[N];
int n;
int dp[N][N][3];
int check(int l,int r){
if((r-l+1)&1) return 0;
int mid=(l+r)>>1;
for(int i=l;i<=mid;i++) if(s[i]!=s[i+mid-l+1]) return 0;
return 1;
}
signed main(){
scanf("%s",s+1); n=strlen(s+1);
for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) dp[i][j][0]=dp[i][j][1]=(j-i+1);
for(int len=2;len<=n;len++)
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
if(check(l,r)) dp[l][r][0]=min(dp[l][r][0],dp[l][(l+r)/2][0]+1);
for(int k=l;k<r;k++)
dp[l][r][0]=min(dp[l][r][0],dp[l][k][0]+r-k);
for(int k=l;k<r;k++)
dp[l][r][1]=min(dp[l][r][1],min(dp[l][k][0],dp[l][k][1])+min(dp[k+1][r][0],dp[k+1][r][1])+1);
}
printf("%d\n",min(dp[1][n][1],dp[1][n][0]));
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9