P2657 [SCOI2009] windy 数

10 篇文章 0 订阅
2 篇文章 0 订阅

windy數 定義:

1. 個位數是一個windy 數

2. 每個相鄰的位數相差為2或以上(必須為整數差值),不存在00146這樣的前導0

觀察定義2,我們知道位數的值影響我們的判斷,所以我們考慮 dp[i][j] 等於 i 位數為j的時候有多少windy數,你可以想像為 0 ~ j*10^(i-1) - 1有多少windy數。

然後你會知道每個區間的windy數都是獨立的,他們符合加法律。所以 對於一個ABCD,我們可以把他拆解為 windy(A000) + windy(B000) + windy(C0) + windy(D) 。

所以只要我們初始化了每個位數以0~9為開始的windy數總數,利用上面的拆解就可以輕鬆得到ABCD的windy總數。

那麼如何初始化呢? ABCD為例,我們知道相差小於2的可能性只有 A 和 A + 1, A - 1,A,這三種可能

f[i] = \sum ^{9}_{|d[k]-d[k + 1]| >1} f[i - 1][k]

上面這個公式就可以幫我們求出部份在 0 ~ A000中的windy數。

那麼忽略的部份又在哪裡呢?

就是對於0135這種情況,如果他不是後綴,而是作為一個獨立的數字,那麼他就是windy 數,但是作為後綴就不是了。然而我們的 f[i][j] 是 i 位數以j開頭的所有windy數總數,所以我們的f 是把0135當成一個數來看,會把這個數判斷成非法情況,所以要弄一個g[i]紀錄這些合法但被判成非法的情況,其實就是把前一位是0或者1的windy總數加起來。

#include <iostream>
using namespace std;
int f[25][10], g[25], a, b;
int cnt(int x){
    if(x <= 0) return 0;
    int len = 0, nums[25];
    while(x){
        nums[++len] = x%10;
        x/=10;
    }
    long long sum = 0;
    for(int i = len; i >= 1; i++){
        if(len == i){
            for(int j = 0; j < nums[i]; j++){
                sum += f[i][j];
            }
            sum += g[i];
            continue;
        }
        for(int j = nums[i + 1] + 2; j < nums[i]; j++){
            sum += f[i][j];
        }
        for(int j = 0; j <= min(nums[i + 1] - 2, nums[i] - 1; j++){
            sum += f[i][j];
        }
        //if previous digits is illegal, unnecessary to continue
        if(abs(nums[i + 1] - nums[i]) < 2)break;
    }
    return sum;
}
void work(){
    cin >> a >> b;
    //0 and 1 haven't counted in
    g[1] = 2;
    for(int i = 0; i <= 9; i++)f[1][i] = 1;
    for(int i = 2; i <= 15; i++){
        //because 0135 and 0035 are kind of windy number when it is not a suffix
        g[i] = f[i - 1][1] + f[i - 1][0] + g[i - 1];
        for(int j = 0; j <= 9; j++){
            //only j, j - 1, j + 1 are illegal
            for(int k = 0; k <= j - 2; k++){
                f[i][j] += f[i - 1][k];
            }
            for(int k = j + 2; k <= 9; k++){
                f[i][j] += f[i - 1][k];
            }
        }
    }
    cout << cnt(b + 1) - cnt(a) << endl; 
}
int main(){
    work();
}

學到了,這種數字符合某種規律,並且在不同位數下符合加法律,就可以把他拆解成A000,B00,C0,D這種形式,再考慮 有沒有因為前導0而發聲錯誤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值