AcWing 1085. 不要62

题目传送门

#include <bits/stdc++.h>

using namespace std;
const int N = 12;//因为n<=m<=1e9 ,所以数位最多是9,开12肯定够
int a[N];        //数位分离的数组
//f[pos][st]表示当前第pos位,这里st只需要去0和1两种状态就可以了,不是6的情况可视为同种,不会影响计数。
int f[N][2];

/**
 * 功能:
 * @param pos 第pos位
 * @param pre 前一位的数字
 * @param st  pre=6?st=1:st=0,这样就把“不受限情况下,前一位是6或不是6的都记忆化存储起来”
 * 现在的理解就是:按集合划分的思想,将位置是pos,前位是6的所有可能存储起来,
 * 把前位不是6的所有可能也存储起来,方便再次使用时直接提供
 * @param limit 是不是贴上界
 * @return
 */
int dfs(int pos, int pre, int st, bool limit) {
    if (pos == -1) return 1;//数组下标从0开始,从前向后搜索,所以pos=-1表示完成了所有搜索
    if (!limit && f[pos][st] != -1) return f[pos][st];//如果可以利用记忆化结果的话
    int up = limit ? a[pos] : 9;    //贴上界:a[pos];不贴上界:9
    int res = 0;                    //符合当前条件的运算结果
    for (int i = 0; i <= up; i++) { //遍历当前位的所有可能选择数字
        //下面两句是题目要求
        if (pre == 6 && i == 2)continue;//如果前面数字是6,当前数字是2,那么不能计数
        if (i == 4) continue;           //4也不要
        //将下一数位计算的结果加到当前数位结果中来
        res += dfs(pos - 1, i, i == 6, limit && i == a[pos]);
    }
    //如果不受限制,则记录下来提供下次使用;如果受限制,则一把一利索
    return (!limit) ? f[pos][st] = res : res;
}

int calc(int x) {
    //数位分离
    int pos = 0;
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }
    //调用dfs返回0~x之内符合条件的数字个数
    return dfs(pos - 1, -1, 0, true);
}

int main() {
    int l, r;
    //初始化
    memset(f, -1, sizeof f);
    while (cin >> l >> r && l + r)  //当输入一行为“0 0”时,表示输入结束
        printf("%d\n", calc(r) - calc(l - 1));
    return 0;
}
上一篇:乘积最大——DP


下一篇:(62)C#里怎么样转换16进制字符串为数字类型?