洛谷 P2657 windy 数(数位DP)
题意:如果在一个不含前导0的数字中,他的相邻两个数字的差不超过2那么我们称之为一个windy数,现在需要求解$$区间内有多少个windy数
解题思路:对每一位上的数字进行预处理可以得当前最高位数字所对应的值,之后依序考虑每一位上的数字是否可一存放当前数字即可,具体解法为:对于比当前位置要小的数字,可以将其所对应的结果全部加上,对于和当前位数一样的数字,我们需要枚举首位数字,来判断是可以直接加入还是需要验证是否可以加入,当找到一个不可加入的数字后,退出。
解题代码:
#include <iostream>
#define int long long
using namespace std;
const int mod = 1e6 + 7;
const int maxn = 1e2 + 20;
int a[maxn];
int dp[maxn][maxn];
int func(int x){
int len = 0;
while(x > 0ll)a[++len] = x % 10,x /= 10;
int sum = 0ll;
//对于位数比当前位数要小的数,那么直接将答案加入即可
for(int i = 1;i <= len - 1;i++){
for(int j = 1;j <= 9;j++){
sum += dp[i][j];
}
}
//对于首位数字比给定数字要小的数,我们也是直接将答案加入
for(int i = 1;i < a[len];i++)sum += dp[len][i];
//对于剩下的数要去判断方案的合法性
//此处枚举位数
for(int i = len - 1;i>=1;i--){
//去找每一位前面最多能放得
for(int j = 0;j <= a[i] - 1;j++){
if(abs(j - a[i + 1]) >= 2)sum += dp[i][j];
}
//如果当前位置和上一位的差值已经比二要小了,那么之后不管怎么摆都是不合法的,可以直接跳出。
if(abs(a[i + 1] - a[i]) < 2)break;
}
return sum;
}
signed main(){
//对于只有一位数的情况,至少为一
for(int i = 0;i <= 9;i++)dp[1][i] = 1ll;
//此处枚举的是位数
for(int i = 2;i <= 10;i++){
//此处枚举的是首位数字,因为数字中的单独一部分可能有前导零,所以此处要从0开始
for(int j = 0;j <= 9;j++){
//此处枚举的是次位的数字,如果次位数字和首位数字相差不超过二
//那么可以将次位数字为k的方案加入到当前方案中
for(int k = 0;k <= 9;k++){
if(abs(k-j) >= 2)dp[i][j] += dp[i - 1][k];
}
}
}
int n,m;
cin >> n >> m;
if(n > m)swap(n, m);
printf("%lld\n",func(m + 1) - func(n));
system("pause");
return 0;
}

浙公网安备 33010602011771号