找出区间[A, B]内所有数字的奇数字位出现次数为偶数,偶数字位出现次数为计数的数的个数。(数位DP)

题目:找出区间[A, B]内所有数字的奇数字位出现次数为偶数,偶数字位出现次数为计数的数的个数。

分析:这道题的状态同样不好取,因为要求每一个奇数的个数都要为偶数,每一个偶数的位数都要为奇数,又因为只有10个数(0~9),又因为没个数只有3种状态,分别是没有(0),奇数个(1),偶数个(2),这样我们就利用3进制进行压缩就可以了,3的10次方不超过60000,因此直接开60000即可,这样dp[i][j]的i表示当前处理到了第i为,j表示当前(0~9)对应的状态

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 25;
typedef long long LL;
LL dp[maxn][60000],bit[maxn];

int judge(int state){   //计算是否满足题意,奇数为偶,偶数为奇
    for(int i = 0; i <= 9; ++i, state/=3) {
        if((i&1)==1&&state%3==1) {
            return 0;
        }
        if((i&1)==0&&state%3==2) {
            return 0;
        }
    }
    return 1;
}

int change(int state,int i){
    int temp = state;
    int j = i;
    while(j--){///取到当前位
        temp /= 3;
    }
    int x = temp%3;
    if(x == 0)
        state += (int)pow(3.0, i);
    else if(x == 1)
        state += (int)pow(3.0, i);
    else
        state -= (int)pow(3.0, i);
    return state;
}

LL dfs(int pos,int state,int limit){
    if(pos < 1) return judge(state);
    LL &ans = dp[pos][state];
    if(!limit && ans != -1) return ans;
    LL ret = 0;
    int len = limit?bit[pos]:9;
    for(int i = 0; i <= len; i++)
        ret += dfs(pos-1, state==0&&i==0?0:change(state, i), limit&&i==len);
    if(!limit) ans = ret;
    return ret;
}

LL solve(LL n){
    int len = 0;
    while(n){
        bit[++len] = n%10;
        n /= 10;
    }
    return dfs(len, 0, 1);
}

int main()
{
    int T;
    scanf("%d", &T);
    LL A,B;
    memset(dp, -1, sizeof(dp));
    while(T--){
        scanf("%lld%lld", &A, &B);
        printf("%lld\n", solve(B)-solve(A-1));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/shuaihui520/p/9938710.html

时间: 2024-10-13 05:26:12

找出区间[A, B]内所有数字的奇数字位出现次数为偶数,偶数字位出现次数为计数的数的个数。(数位DP)的相关文章

hdu3709(求区间内平衡数的个数)数位dp

题意:题中平衡数的定义: 以一个位置作为平衡轴,然后左右其他数字本身大小作为重量,到平衡轴的距离作为全职,实现左右平衡(即杠杆原理平衡).然后为区间[x,y]内平衡数的个数. (0 ≤ x ≤ y ≤ 1018) 解法:数位dp.如果一个数的平衡数,那么它的平衡轴位置是确定的.原来一直尝试数位dp在dfs时候列举平衡轴的位置,后来才意识到可以提前枚举平衡轴位置,然后再dfs,这样比较好写.dp[mid][pre][wei];表示对称轴是mid,计算第pre个位置以后需要力矩大小wei的数的个数.

c语言:找出1到4000中,数字的各位数之和能被4整除的数有多少个?

找出1到4000中,数字的各位数之和能被4整除的数,如:745:7+4+5=16,16可以被4整除:28:2+8=10,10不能被4整除:745就是这样的特殊数字,而28不是,求:这样的数字共有多少个? 解: (1)对于4000,4+0+0+0=4,显然4000是满足条件的数字: (2)对于1到3999,我们把每个数字看成4位[][][][]的形式,第一位[]取0到3,后3位取[0][0][0]到[9][9][9],用sum表示4位数字的和: 2.1:若后3位为一个奇数,则第1位取1或3,必定可

log(n)时间内找出数组第i小的数字

参考算法导论9.2 R_Select(int *a,int p,int r,int i){ if(p==r) return a[p]; int q=partition(a,p,r); int k=q-p; if(i==k) return a[q]; else if(i<k) return R_Select(a,p,q-1,i); else return R_Select(a,q+1,r,i-k); } log(n)时间内找出数组第i小的数字

Java 找出四位数的全部吸血鬼数字 基础代码实例

/** * 找出四位数的全部吸血鬼数字 * 吸血鬼数字是指位数为偶数的数字,能够由一对数字相乘而得到,而这对数字各包括乘积的一半位数的数字,当中从最初的数字中选取的数字能够随意排序. * 以两个0结尾的数字是不同意的. *   比例如以下列数字都是吸血鬼数字 1260=21*60 1827=21*87 2187=27*81 ... * 比較笨的低效率的做法: 遍历全部四位数, 每生成一个四位数的时候, *         在双重循环遍历两位数,在两位数的内层循环中推断是否与最外层循环的四位数相等

51Nod 1009 数字1的个数 | 数位DP

题意: 小于等于n的所有数中1的出现次数 分析: 数位DP 预处理dp[i][j]存 从1~以j开头的i位数中有几个1,那么转移方程为: if(j == 1) dp[i][j] = dp[i-1][9]*2+pow(10,i-1);else dp[i][j] = dp[i-1][9]+dp[i][j-1]; 然后注意下对于每个询问统计的时候如果当前位为1需要额外加上他后面所有位数的个数,就是n%pow(10,i-1); 这样总复杂度log(n)*10 #include <bits/stdc++.

面试40-一个数组,有2个数字出现奇数次,其余都是偶数次,求这两个数字O(n) O(1)

#include<iostream> using namespace std; // 题目:数组中只有不多于两个数字出现次数是奇数次,其他都是偶数次,求出出现奇数次的数字(不含0的数组) //思想: /* (1)如果只有一个数字是奇数次,直接对数组进行按位异或运算,得到的结果就是该数 (2)如果有俩个,可以先对数组异或,得到的结果(就是两个奇数次的数字异或的结果),必定至少包含一个1,可以根据这个1在的位置,把数组分为两个部分 则两个奇数次的数字必定分别在两个部分,而相同的数次必定在同一组,则

使用二重循环和break语句,找出2~100内所有的质数

public static void main(String[] args) { int m = 0; for(int i = 2 ; i < 100 ; i++){ for(int j = 2;j <= Math.sqrt(i)+1 ;j++){ m=i % j; if(m == 0){ break; } } if(m!=0) System.out.print(i+" "); } } }

找出一个整形数组中第二大的数字

如何在时间复杂度为O(n)内找出数组中第二大的数字? 通过设置两个变量,一个保存最大值,一个保存第二大值,通过在找最大值的过程中,原来的最大值逐渐变为第二大值.一种实现代码如下(Java版): 1 /** 2 * 在时间复杂度为O(n)内找出数组的第二大的数字 3 * @author JiaJoa 4 * 5 */ 6 public class Algorithm_GetSecondMax { 7 8 public static void main(String[] args) { 9 // T

HDU 5327 区间里由不同的数字组成的数的个数-set-(枚举)

题意:形如12345是我们要找的数,形如11是不合法的数,找出区间[a~b]里的合法数的个数,1<=a<=b<=10^5 分析:预处理,枚举从1开始到i结束的区间的合法数的个数,然后输入一个区间就输出sum[b]-sum[a-1]即可.这里借助set工具,利用它的count()函数,枚举数i,判定它是否为合法的数,方法是取它的每一位,然后在set里对这一位计数,如果 !=0,说明set里已经有相同的数字了,那么这个数就不合法,否则就插入这一位的数字. STL大法好!要善于运用 代码: #