P9232 [蓝桥杯 2023 省 A] 更小的数
区间 DP 做法
其实要满足
设双指针
- 若
l>r ,说明区间[i,j] 为回文串,翻转前后相等,dp_{i,j}=0 ,退出判断。 - 若
a_l<a_r ,则当前判断的区间[l,r] 翻转后的最高位较大,dp_{i,j}=0 ,退出判断。 - 若
a_l>a_r ,则当前判断的区间[l,r] 翻转后的最高位较小,dp_{i,j}=1 ,退出判断。 - 若
a_l=a_r ,则a_l,a_r 对dp_{i,j} 的值没有影响,将双指针指向的区间[l,r] 缩小到[l+1,r-1] ,即执行l\gets l+1,r\gets r-1 。
如果像上述做法暴力判断的话,复杂度最差为
- 若
a_i<a_j ,则dp_{i,j}=0 。 - 若
a_i>a_j ,则dp_{i,j}=1 。 - 若
a_i=a_j ,则dp_{i,j}=dp_{i+1,j-1} 。
这就是区间 DP,复杂度为
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=5010;
char s[maxn];
int dp[maxn][maxn];
int main(){
scanf("%s",s);
int n=strlen(s),ans=0;
for(int i=0;i<n;i++) dp[i][i]=0;
for(int i=0;i<n-1;i++) dp[i][i+1]=(s[i]>s[i+1]);
// 初始值
for(int len=3;len<=n;len++){
for(int i=0;i<n-len+1;i++){
int j=i+len-1;
if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
else if(s[i]>s[j]) dp[i][j]=1;
}
}
for(int i=0;i<n;i++){
for(int j=i;j<n;j++) ans+=dp[i][j];
}
//求总数
printf("%d\n",ans);
return 0;
}