zoukankan      html  css  js  c++  java
  • [SCOI2009]windy数

    题意

    Here

    思考

    写的第一道数位 (dp) 的题,因为感觉最近比较冷门所以一直没学 (QWQ) 我太菜了。

    首先,(l) ~ (r)(windy) 数可以转成 (1) ~ (r)(windy) 数减去 (1) ~ (l-1)(windy) 数 (前缀和)

    如何求 (1) ~ (k)(windy) 数呢?我是用记忆化搜索实现的,先考虑要用哪些状态来表示:

    1. 当前搜到第几位数 (pos),(如果超过了位数就返回)
    2. 由于题中的 (windy) 数相邻两位之间有关系,那么我们需要记录上一位数是什么,才能判断要不要统计答案,所以还要记录该位的上一位 (pre)
    3. 由于有最大值,如果前一位已经被限制了,那么下一位的最大值也有限制,例如:求 (1) ~ (19260817) 的满足条件的数的个数,前面已经搜到了 (19******),第三位就只能枚举到 (2) 而不是 (9)
    4. 由于位数是不定的,但我们还是要从最高位开始搜,所以会有前导零,我们还要记录该位前一位是否有前导零(即继续判断该位是否是最高位/前导零)。

    状态表示已经清楚,下面就开始转移了:

    首先限制枚举的最大值

    ll MAX = limit ? a[pos] : 9;//limit(0/1)表示是否有限制,a[pos]是pos位上的最大值
    

    接下来枚举该位为 (0) ~ (MAX)

    for(ll i=0; i<=MAX; i++){
        if(!lead && abs(i - pre) < 2) continue;
        if(!i && lead) ans += dfs(pos-1, 0, 1, i==MAX&&limit);
        else if(i && lead) ans += dfs(pos-1, i, 0, i==MAX&&limit);
        else ans += dfs(pos-1, i, 0, i==MAX&&limit);
    }
    
    1. 如果不是第一位并且不满足条件,不统计答案
    2. 如果当前位选择 (0) 并且有前导零,那么该位也是前导零,统计答案
    3. 如果该位不是零但有前导零,那么该位为最高位,统计答案
    4. 其他状况,满足条件,统计答案

    至此,我们就能 (AC) 本题了~

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll dp[20][20], len, a[20];
    ll dfs(ll pos, ll pre, ll lead, ll limit){
        if(pos == 0) return 1;
        if(!lead && !limit && dp[pos][pre] != -1) return dp[pos][pre];
        ll ans = 0;
        ll MAX = limit ? a[pos] : 9;
        for(ll i=0; i<=MAX; i++){
            if(!lead && abs(i - pre) < 2) continue;
            if(!i && lead) ans += dfs(pos-1, 0, 1, i==MAX&&limit);
            else if(i && lead) ans += dfs(pos-1, i, 0, i==MAX&&limit);
            else ans += dfs(pos-1, i, 0, i==MAX&&limit);
        }
        if(!lead && !limit) dp[pos][pre] = ans;
        return ans;
    }
    ll part(ll x){
        memset(dp, -1, sizeof(dp));
        ll len = 0;
        while(x){
            a[++len] = x % 10; x /= 10;
        }
        return dfs(len, 0, 1, 1);
    }
    ll x, y;
    int main(){
        cin >> x >> y;
        cout << part(y) - part(x-1);
        return 0;
    }
    
    

    总结

    注意我们是在没有限制最大数的情况下才记忆化:由于有限制条件时,满足条件的数肯定会比无限制的时候少(就是他们的个数不相等),所以无法记忆化

  • 相关阅读:
    C语言warning的收集和总结
    HLS:跑马灯实验
    HLS:OpenCV和RTL代码转换关系
    Zynq-7000 FreeRTOS(二)中断:串口Uart中断
    Zynq-7000 FreeRTOS(二)中断:PL中断请求
    Zynq-7000 FreeRTOS(二)中断:Timer中断
    xilinx DMA IP核(二) —— 文档阅读
    Zynq-7000 FreeRTOS(一)系统移植配置
    xilinx DMA IP核(一) —— loop测试 代码注释
    System Verilog基础(二)
  • 原文地址:https://www.cnblogs.com/alecli/p/9927174.html
Copyright © 2011-2022 走看看