洛谷P2657 [SCOI2009]windy数
数位DP
数位DP一般用于对满足一个什么条件的数的个数进行计数,且该数的数据范围很大的问题进行求解。这种题的状态一般都跟长度、限制条件、最高位上的数有关。
本题的状态为:dp[i][j]为长度为i的,最高位为j的windy数个数。
此状态有个非常简单的递推式,dp[i][j]=∑dp[i−1][k](abs(j−k)<=2)
然后我们用函数solv(a)返回1到a−1的所有windy数的个数,最终答案即为solv(b+1)−solv(a)的值。
考虑solv函数该如何写,现将a逐位分解,求出len个数,然后将长度比len小无论最高位不为0的(因为不含前导零)dp值都加上,因为他们肯定都属于1到a−1之中,然后再加上长度为len且最高位比j小的windy数个数,然后此时我们存在漏解的情况,我们还少长度为len且最高位跟j相同,但是又比a小的windy数个数,因为此时len为位是相同的,所以我们直接枚举len-1到1位,每次确定一位然后加上当前枚举位的dp值,如果当前位在枚举完了之后确定以后跟上一位相差小于二,则break,因为此位已确定且已经不满足条件了。
//long long,inline后加函数类型
//关键字:y1,time,tm,end,next,hash, j0,j1,jn,y0,yn,_end会re;
#include <bits/stdc++.h>
#define int long long
using namespace std;
long long A, B;
int dp[1001][1001], temp[1001], len;//dp[i][j]代表长度i,最高位为j的windy数个数(注意是最高位)
void init()
{
for (int i = 0; i <= 9; i++)
dp[1][i] = 1;
for (int i = 2; i <= 10; i++)
for (int j = 0; j <= 9; j++)
for (int k = 0; k <= 9; k++)
if (abs(j - k) >= 2)
dp[i][j] += dp[i - 1][k];
}
long long solv(int a)//返回n的数
{
memset(temp, 0, sizeof(temp));
len = 0;
int ans = 0;
while (a)
temp[++len] = a % 10, a /= 10;
for (int i = 1; i <= len - 1; i++)//求len-1位的符合条件的windy数。
for (int j = 1; j <= 9; j++)
ans += dp[i][j];
for (int j = 1; j < temp[len]; j++)
ans += dp[len][j];
for (int i = len - 1; i >= 1; i--)//求len-1所有跟a相差不大的数。
{
for (int j = 0; j < temp[i]; j++)
if ( abs(j - temp[i + 1]) >= 2)
ans += dp[i][j];
if (abs(temp[i] - temp[i + 1]) < 2) break;//只要有一次相邻的两个数不满足则不需要再向下继续搜索下去了。
}
return ans;
}
signed main()
{
init();
scanf("%lld%lld", &A, &B);
printf("%lld", solv(B + 1) - solv(A));
return 0;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步