LG-P2657 [SCOI2009]windy數
阿新 • • 發佈:2018-11-05
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;
}