//整体的思路:
//先知道1-i刷成b最少需要多少次
//然后知道
//dp数组提供了任意区间由A转换成B的最小次数
//f数组实际考虑b的情况来决定是否采用dp提供的数据
//dp数组:提供任意区间 [i, j] 的局部最优解,不考虑全局情况。
//f数组:在全局范围内优化,决定是否采用 dp 提供的局部最优解,同时考虑分段优化和已满足条件的字符。
//f数组的存在是为了将 dp 数组的局部最优解整合到全局最优解中,进一步优化最终结果。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e2+9;
ll dp[N][N],f[N];
char a[N],b[N];
//dp[i][j]的含义:从i-j区间内刷成b需要的最小次数 dp表示一个局部的最优解
//f[i]表示从1刷到i的代价 表示一个全部的最优解
int main()
{
cin>>a+1>>b+1;
int n=strlen(a+1);
memset(dp,0x3f,sizeof(dp));//初始化为最大值
for(int i=1;i<=n;i++ ) dp[i][i]=1;
//区间DP 求1-i刷成b需要最少需要多少次
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r = len + l -1 ;
//这里就是刷成b需要多少次
if(b[l]==b[r])dp[l][r]=min(dp[l+1][r],dp[l][r-1]);
//这里为什么是b不是a需要好好的体会一下
//如果两个端点相等就可以连带一起刷上
for(int k=l;k<r;k++)
{
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}//字符串A和B的理解
//B就是目标的样子,A就是现在的状况,“B在时间上是A的继承样子”
for(int i=1;i<=n;i++) f[i]=dp[1][i]; //默认全部需要刷,不存在相等的位置
//但实际上存在一些地方有相同之处,所以要讨论
for(int i=1;i<=n;i++)//右端点从1到n进行遍历
{
if(a[i]==b[i]) f[i]=min(f[i],f[i-1]);
for(int j=1;j<i;j++) f[i]=min(f[i],f[j]+dp[j+1][i]);
//应该还是插点,这里的j相当于k,因为左端点定了.
//从j+1-i的区间不刷,从1-j的区间可能刷,也可能不刷
}
cout<<f[n]<<endl;
return 0;
}
最少操作数
于 2025-01-20 23:16:23 首次发布