1. 程式人生 > >LG-P2657 [SCOI2009]windy數

LG-P2657 [SCOI2009]windy數

P2657 [SCOI2009]windy數
題目連結
題目描述
windy定義了一種windy數。不含前導零且相鄰兩個數字之差至少為2的正整數被稱為windy數。 windy想知道,

在A和B之間,包括A和B,總共有多少個windy數?

輸入格式:
包含兩個整數,A B。

輸出格式:
一個整數

輸入樣例:
1 10
輸出樣例:
9
輸入樣例:
25 50
輸出樣例:
20
說明
100%的資料,滿足 1 <= A <= B <= 2000000000 。

題解
數位DP的模板題,也是我A的第一道數位DP。
首先肯定想到利用容斥,1 到 B 的windy數 減去 1 到 A-1 的windy數 等於 A 到 B 的windy數。

對於一個 x,求直接求小於它的windy數比較麻煩,所以我們分成若干個板塊來求。

程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=15,maxp=15;
int A,B,f[maxn][maxn],a[maxn];
//f[i][j]表示有i位,且最高位位j的windy樹個數 
int ab(int x){return x<0?-x:x;}
long long
get_(int x) { a[0]=0;int y=x; while (y) a[++*a]=y%10,y/=10; //DP 刷好了,準備開始累積答案。 //為了保持清晰的思路,我們分成三個部分來求 long long ans=0; //第一部分是位數小於x的windy數 for (int i=1;i<*a;++i) for (int j=1;j<10;++j) ans+=f[i][j]; //第二部分是位數和x相等,但是最高位小於x的windy數 for (int j=1;j<a[*a];++j) ans+=f[*a][j]; //第三部分是位數和x相等,且若干高位與x相等,但是小於x的windy數
//也就是貼著x,把那些漏掉的windy數一點一點摳出來 for (int i=*a-1;i;--i)//i列舉不相等的最高位 { for (int j=0;j<a[i];++j) //由於i之前的位已經定了,所以直接疊加f[i][j],此時是這位有可能為0 if (ab(j-a[i+1])>=2) ans+=f[i][j]; //如果這一位不滿足,就沒必要繼續了 if (ab(a[i+1]-a[i])<2) break; } return ans; } int main() { memset(f,0,sizeof f); scanf("%d%d",&A,&B); //只有一位的必定為windy樹 for (int i=0;i<10;++i) f[1][i]=1; //頂多10位 for (int i=2;i<=10;++i) for (int j=0;j<10;++j)//你可能會問高位怎麼能為0呢?看work裡的Part3 for (int t=0;t<10;++t) if (ab(j-t)>=2) f[i][j]+=f[i-1][t]; printf("%lld",get_(B+1)-get_(A)); return 0; }