这两道都是区间dp的问题,现总结一下区间dp的模班便于今后复习。
先直接上蓝桥杯 密码脱落
/*这题意思是给我们一个字符串序列,问我们需要给它在其中至少添加多少个字符,才能使得它是回文串
题目其实等价于在这个序列中,找到最大长度的回文子序列
这里的添加操作其实和删除操作是等价的,添加几个字符使得这个串是回文串,添加的字符个数等价于删除几个字符使得这个串是回文串,删除的字符个数。
最终至少需要添加的字符个数==字符串总长度−−字符串中的最长回文子序列长度*/
// 上述的等价替换要考虑到
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int f[N][N]; // 状态表示为i和j区间内最长的回文子序列
// 为区间dp问题
int main()
{
string s;
cin>>s;
int n=s.size(); // 先让n等于序列长度
for(int len=1;len<=n;len++) // 先枚举区间长度
{
for(int i=0;i+len-1<n;i++) // 再枚举区间左端点
{
int j=i+len-1; // 枚举区间右端点
if(len==1) f[i][j]=1; // 特判一下,永远不要忘了特判
else
{
f[i][j]=max(f[i+1][j],f[i][j-1]); // 表示有s[i]无s[j]和无s[i]有s[j],取最大值
if(s[i]==s[j]) f[i][j]=max(f[i][j],f[i+1][j-1]+2); // 表示s[i]和s[j]相等,即均存在
}
}
}
cout<<n-f[0][n-1]<<endl; // 最后返回删除的字符数量,f[0][n-1]是序列中最长的回文序列,是留下来的,所以要用n-f[0][n-1]
return 0;
}
再上 石子合并 ,两者对比,感悟一下区间dp模板
#include<iostream>
using namespace std;
const int N=310;
int s[N];
int f[N][N],n;
// 区间长度模板就是如此。
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>s[i];
s[i]+=s[i-1];
}
for(int len=2;len<=n;len++) //区间dp问题先枚举区间长度 ,再左端点,再右端点
{
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
f[i][j]=1e8;
for(int k=i;k<j;k++)
{
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
cout<<f[1][n]<<endl;
return 0;
}