P2657 [SCOI2009]windy数
题目描述
windy定义了一种windy数。不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,
在A和B之间,包括A和B,总共有多少个windy数?
输入格式
包含两个整数,A B。
输出格式
一个整数
输入输出样例
输入 #1
1 10
输出 #1
9
输入 #2
25 50
输出 #2
20
说明/提示
100%的数据,满足 1 <= A <= B <= 2000000000
Solution
数位dp。
因为影响决策的是前一位数的大小。所以令dp[i][j]为当前搜到第i为且前一位为j时候的方案数。求出小于等于b的方案数 - 小于等于a - 1的方案数为答案。当出现前导零的时候,下一位可以任意取,即1,2…这种一位数也是答案,为了方便处理,我们可以直接把前导零赋值为-2,这样下一位可以任意取。
在记忆化(记录方案)的时候,只记忆普遍的情况,对于前导零和到上限的情况单独计算,因为它们的计算有限制条件,与普通情况不同。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int SZ = 10 + 5;
ll l,r,temp;
int num[SZ],dp[SZ][SZ];
inline ll dfs(int pos,int pre,int lead,int limit)
{
if(pos > temp) return 1;
if(!lead && !limit && dp[pos][pre] != -1) return dp[pos][pre];
ll sum = 0;
int up = limit?num[temp - pos + 1]:9;
for(int i = 0;i <= up;i ++ )
{
if(abs(i - pre) < 2) continue;
if(lead && i == 0) sum += dfs(pos + 1,-2,1,limit && i == up);
else sum += dfs(pos + 1,i,0,limit && i == up);
}
if(!limit && !lead) dp[pos][pre] = sum;
return sum;
}
inline ll solve(ll x)
{
temp = 0;
while(x)
{
num[ ++ temp] = x % 10;
x /= 10;
}
memset(dp,-1,sizeof(dp));
return dfs(1,-2,1,1);
}
int main()
{
scanf("%lld%lld",&l,&r);
ll ans = solve(r) - solve(l - 1);
printf("%lld\n",ans);
return 0;
}
2020.3.16