1. 程式人生 > 實用技巧 >[SCOI2009] windy 數 (數位dp)

[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]的方案數 
}