[SCOI2009] windy 数 (数位dp)

题目

算法

应该是一道很经典的数位dp题
我们设dp[i][j]是填到第i位此时第i位的数是j的方案数
然后进行转移(代码注释)

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
ll p,q,dp[15][15];
ll init(){//进行初始化 
   for(ll i = 0;i <= 9;i++) dp[1][i] = 1;//[0,9]显然都是windy数 
   for(ll i = 2;i <= 10;i++) 
   for(ll j = 0;j <= 9;j++)
   for(ll k = 0;k <= 9;k++)
   if(abs(j - k) >= 2) dp[i][j] += dp[i - 1][k];//先预处理好dp值 
}
ll work(ll x){//统计答案 
	ll a[15],len = 0,ans = 0;
	while(x){//将x分解 
		a[++len] = x % 10;
		x /= 10;
	}
	for(ll i = 1;i <= len - 1;i++)//先统计位数不足x位数的数 那这些数明显都可以计算到方案中 
	for(ll j = 1;j <= 9;j++)
	ans += dp[i][j];
	for(ll i = 1;i < a[len];i++)//位数和x位数相同 但最高位比x最高位小 显然也可以 
	ans += dp[len][i];
	for(ll i = len - 1;i >= 1;i--){//这里处理位数和x位数相同 最高位 = x最高位的情况 
	for(ll j = 0;j <= a[i] - 1;j++)
	if(abs(j - a[i + 1])>= 2)  ans += dp[i][j];
	if(abs(a[i + 1] - a[i]) < 2) break;
	}
	return ans;
}
ll a,b;
int main(){
	scanf("%lld%lld",&a,&b);
	init();
	cout<<work(b + 1) - work(a);//这里应用前缀和的思想 work计算[0,x)的方案数 那么用work(b + 1) - work(a) 就是[a,b]的方案数 
}
原文地址:https://www.cnblogs.com/dixiao/p/13804892.html