-数位DP

目录

1、 数位和 

2、不要62

3、数数1

4、数数2

5、数数3


1、 数位和 

// 数位和

#include<bits/stdc++.h>
using namespace std;
int c[21];
long long l, r, ans[10], f[17];

inline void calc(long long n, int xs){  // xs : 系数
    int m = 0;           
    while(n){
        c[++m] = n % 10;
        n /= 10;
    }
    reverse(c + 1, c + 1 + m); // 3 2 4

    for(int i = 1; i <= m; ++i){
        // pos < i
        for(int j = 1; j < i; ++j)
            ans[c[j]] += xs * c[i] * f[m - i];
        // pos = i
        if(i != 1 && c[i]) // c[i]的意思是c[i]这个数比0大  因为范围是  [0,c[i]),所以c[i]比0大,才考虑给0做贡献
            ans[0] += xs * f[m - i];
        for(int j = 1; j < c[i]; ++j)
            ans[j] += xs * f[m - i];
        // pos > i
        if(m > i){
            if(i != 1)
                ans[0] += xs * c[i] * f[m - i - 1] * (m - i);
            for(int j = 1; j < 10; ++j)
                ans[j] += xs * c[i] * f[m - i - 1] * (m - i);
        }
    }
    // 单独考虑第一类情况时,0的个数有多少个
    // 统计0的个数时,如果不单独考虑第一类的话,前导0也会被记入。  0000 - 9999   其实是 0 - 9999
    if(m > 1)
        ans[0] += xs * (c[1] - 1) * f[m - 2] * (m - 1);
    // 第1位非 0 的数字在第 j 位
    for(int j =  2; j < m ; ++j)
        ans[0] += xs * 9 * f[m - j - 1] * (m - j);
}

int main(){
    f[0] = 1;
    for(int i = 1; i < 17; ++i)
        f[i] = f[i - 1] * 10;  // 存幂
    scanf("%lld%lld",&l,&r);
    calc(r + 1, 1);
    calc(l, -1);
    for(int i = 0; i <= 9 ; ++i)
        printf("%lld ", ans[i]);
    cout<<endl;

    return 0;
}

2、不要62


// 不要62
// #include <bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll l, r, f[20][2];
int c[21];

ll calc(ll n){
	int m = 0;
	memset(c,0,sizeof(c));
	while(n){
		c[++m] = n % 10;
		n /= 10;
	}
	reverse(c + 1, c + 1 + m);
	bool ok = true;
	ll res = 0;
	for(int i = 1; i <= m && ok; ++i){
		for(int j = 0; j < c[i]; ++j){
			memset(f,0,sizeof(f));
			if(j == 4 || (c[i - 1] == 6 && j == 2))
				continue;
			if(j == 6)
				f[i][1] = 1; // 前i位,第i位为6  则第二维为1
			else
				f[i][0] = 1;
			for(int k = i + 1; k <= m; ++k)
				for(int sign = 0; sign < 2; ++sign)
					if(f[k - 1][sign])
						for(int l = 0; l < 10; ++l){
							if(l == 4 || (sign && l == 2))
								continue;
							if(l == 6)
								f[k][1] += f[k - 1][sign]; 
							else
								f[k][0] += f[k - 1][sign]; 
						}
			res += f[m][0] + f[m][1];

		}
		if(c[i] == 4 || (c[i - 1] == 6 && c[i] == 2))
			ok = false;
	}
	return res;
}
int main(){
	while(~scanf("%lld %lld",&l, &r)){
		if(l == 0 && r == 0)break;
		cout << calc(r + 1) - calc(l) << endl;
	}
	return 0;
}

3、数数1

// 数数 1

#include<bits/stdc++.h>
using namespace std;

#define ll long long
ll l, r, f[21][10][2];
int c[21];

inline ll calc(ll n){// 计算[1,n]满足条件的个数
	if(!n)return 0;// n如果等于0 , 满足条件个数为0
	int m = 0;
	while(n){
		c[++m] = n % 10;
		n /= 10;
	}
	reverse(c + 1, c + 1 + m);
	bool ok = true;
	ll res = 0;


	for(int i = 1; i <= m && ok; ++i){// 第 i 类, 前 i - 1 位的数位值已经固定
		for(int j = 0; j < c[i]; ++j){// 第i位的值为j
			if(i != 1 && abs(j - c[i - 1]) > 2)
				continue;

			// 对于这部分的初始化问题,其实是这样的。
			/*
			比如说计算  12345 这个数字有多少是满足条件的,又比如说我们此时i = 3
			所以我们的j的取值范围是[0,2]
			当j = 0时:
			120 这个三位数是一个满足条件的数。所以f[3][0][1] = 1;
			此后的k 可以取到 4, 5 ,也就是说还要填 4,5这两个坑。而3这个位置的数可能取到[0,9]
			且sign可能是 0 , 1    这里的for循环只是罗列出很多的可能条件,可能很多是冗余的,
			但我们就是全部都考虑到了,冗余(不满足的部分)会因为判断条件而过滤掉
			*/
			memset(f,0,sizeof(f));
			if(i == 1 && !j)
				f[1][0][0] = 1;
			else
				f[i][j][1] = 1;

			for(int k = i + 1; k <= m; ++k){// 后续还有 这么多个坑要填
				for(int l = 0; l < 10; ++l){// 前一位的数字的取值范围可能是[0,9]
					for(int sign = 0; sign < 2; ++sign){
						// 我们填写第k位时,是基于第k-1位满足条件的基础上!
						if(f[k - 1][l][sign]){ // 自己写的时候给漏了!!!
							for(int x = 0; x < 10; ++x){ //k 这个坑可以填的数字的范围是[0,9]
								if(sign && abs(x - l) <= 2)
									f[k][x][1] += f[k-1][l][1];
								if(!sign){
									if(x)
										f[k][x][1] += f[k - 1][0][0];
									else
										f[k][0][0] += f[k - 1][0][0];
								}
							}
						}
					}
				}
			}
			for(int i = 0; i < 10; ++i)
				res += f[m][i][1];

		}
		// 如果出现125XXX  这样的情况, 因为后续的计算是基于前i为依据固定了的
		// 所以,后面的数字肯定不满足条件。  这是一个剪枝操作! 去掉的话,结果也是对的。
		if(i != 1 && abs(c[i] - c[i-1]) > 2)
			ok = false; 

	}
	if(ok)res++;
	return res;
}
int main(){
	scanf("%lld%lld",&l, &r);
	printf("%lld\n",calc(r) - calc(l - 1));
	return 0;
}

4、数数2

// 数数 2

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll l, r, f[20][201];
bool b[201];
int c[21];
ll calc(ll n){ // 计算[1,n)符合条件的
	int m = 0;
	while(n){
		c[++m] = n % 10;
		n /= 10;
	}
	reverse(c + 1, c + 1 + m);

	ll res = 0;
	int sum = 0;
	for(int i = 1; i <= m; ++i){
		for(int j = 0; j < c[i]; ++j){
			memset(f,0,sizeof(f));
			f[i][sum + j] = 1;
			for(int k = i + 1; k <= m; ++k)
				for(int l = 0; l <= (k - 1) * 9; ++l)
					if(f[k - 1][l])
						for(int x = 0; x < 10; ++x)
							f[k][l + x] += f[k - 1][l];
			for(int k = 2; k <= 9 * m; ++k)
				if(!b[k])
					res += f[m][k];
		}
		sum += c[i];
	}
	return res;
}

int main(){
	// ture : 合数   false : 质数
	memset(b,false,sizeof(b));
	b[1] = true;
	for(int i = 2; i <= 200; ++i){
		if(!b[i])
			for(int j = i + i; j <= 200; j += i)
				b[j] = true;
	}
	cin >> l >> r;
	cout << calc(r + 1) - calc(l) << endl;	
	return 0;
}

5、数数3


// 数数 3
#include <bits/stdc++.h>
using namespace std;
#define ll long long
// f[i][j][k][0..1] : 考虑了前 i 位,第 i 位的数字为 j ,前 i 位的状态为 k ,前 i 为是否全为0
// 0 这个状态表示
ll l, r, f[20][10][3][2]; 
int c[21];

int get_status(int status, int x, int y){
    if(status == 2)
        return 2;
    if(x >= y)
        return 0;
    return status + 1;
}
ll calc(ll n){
    int m = 0;
    memset(c, 0 , sizeof(c));
    while(n){
        c[++m] = n % 10;
        n /= 10;
    }
    reverse(c + 1, c + 1 + m);

    ll res = 0;
    int status = 0; // 表示前 i - 1 位的状态
    for(int i = 1; i <= m; ++i){
        for(int j = 0; j < c[i]; ++j){
            int ns = get_status(status,c[i - 1], j); // next_status;
            if(i == 1)
                ns = 0;
            memset(f, 0, sizeof(f));
            if(i == 1 && j == 0)
                f[i][j][0][0] = 1;
            else
                f[i][j][ns][1] = 1;
            for(int k = i + 1; k <= m; ++k){
                for(int w = 0; w < 10; ++w){ // 前一个数的数字范围
                    for(int l = 0; l < 3; ++l){ // 前一个数的 状态可能
                        for(int q = 0; q < 2; ++q){ // 前一个数 是否全为0 的情况可能
                            if(f[k - 1][w][l][q]){
                                for(int x = 0; x < 10; ++x){
                                    if(q == 0 && x == 0)
                                        f[k][0][0][0] += f[k - 1][0][0][0];
                                    else{
                                        if(q)
                                            f[k][x][get_status(l, w, x)][1] += f[k - 1][w][l][q];
                                        else
                                            f[k][x][0][1] += f[k - 1][w][l][q];
                                    }
                                }
                            }
                        }
                    }
                }
            }
            for(int k = 0; k < 10; ++k)
                res += f[m][k][2][1];
        }
    	if(i == 1)
    		status = 0;
    	else
    		status = get_status(status,c[i - 1], c[i]);
    }
    return res;

}
int main(){
    cin >> l >> r;
    cout<< calc(r + 1) - calc(l) << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xingxg.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值