Q - count 数字计数 (数位dp)

本文介绍了一种解决区间内所有整数中0~9每个数字出现次数的问题的算法实现,通过递归分解和数位DP两种方法进行解答,并提供了完整的C++代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Q - count 数字计数

 

题目大意:求[a,b]间所有的整数中0~9每个数字出现了几次

令f[i]为i位数(算前导零)中每个数出现的次数(一定是相同的,所以只记录一个就行了)

有f[i]=f[i-1]*10+10^(i-1)

然后照例十进制拆分

其中计算[0,999...9]的时候要从1~9枚举最高位,然后其余位调用f[i-1]即可

剩余部分已知位直接乘,未知位调用f[i]


转自:http://blog.youkuaiyun.com/PoPoQQQ/article/details/40075787

感觉自己菜到爆。。。。。。看题解都没搞明白,搞了一晚上,。。。交上代码了、、、、

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
typedef long long ll;
using namespace std;
ll ans[10],f[20];

void cal(ll x,ll pos)
{
	while(x){
		ans[x%10]+=pos;
		x/=10;
	}
}

void dfs(ll x,int flag)
{
	int i,j;
	ll pos,now;
	for(i=1,pos=10;pos<x;++i,pos*=10)
	{
		for(j=0;j<=9;j++)
			ans[j]+=f[i-1]*9*flag;
		for(j=1;j<=9;j++)
			ans[j]+=pos/10*flag;
	}
	now= pos/=10;
	--i;
	while(now<x)
	{
		while(now+pos<=x)
		{
			ll temp=now/pos;
			cal(temp,pos*flag);
			for(j=0;j<=9;j++)
				ans[j]+=f[i]*flag;
			now+=pos;
		}
		pos/=10;
		--i;
	}
}
int main()
{
	int i;
	ll a,b,pos;
	f[1]=1;
	for(i=2,pos=10;i<=12;i++,pos*=10)
		f[i]=f[i-1]*10+pos;
	scanf("%lld %lld",&a,&b);
	dfs(b+1,1);
	dfs(a,-1);
	for(i=0;i<=9;i++){
		printf("%lld",ans[i]);
		if(i==9)printf("\n");
		else printf(" ");
	}
	return 0;
} 


代码数位dp:

/*求出区间内0~9的个数
*/
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
int dp[10][10][10]; //dp[i][j][k] 表示以j开头i位数字 含有数字k的个数 
int ans[10][2];
int pos = 0;
vector<int> v;

int work(int n)  //求10^n 
{
    int s = 1, i;
    for(i=1; i<=n; i++)
    {
        s *= 10;        
    }
    return s;
}

int js(int j)   //求v[j-1]~v[0]组成的数字 
{
    int s = 0;
    int i;
    for(i=j; i>=0; i--)
    {
        s = s* 10 + v[i];   
    }   

    return s;
}

void solve(int x)
{
    v.clear();
    while(x > 0)
    {
        v.push_back(x % 10);
        x /= 10;    
    }   

    int i, j, k;
    for(i=v.size(); i>0; i--)
    {
        for(j=v[i-1]-1; j>=0; j--)
        {
            for(k=0; k<=9; k++)
            {
                ans[k][pos] += dp[i][j][k];

                if(j==0 && i==v.size() && k==0) //第一位数字取0减去这次0在第一位出现的个数
                                                //以及后面每次0在首位的情况。 
                {
                    int t = work(v.size()-1);
                    while(t > 1){
                        ans[k][pos] -= t;
                        t /= 10;
                    }

                }
            }
        }
        ans[v[i-1]][pos] += js(i-2);    //第i位数字取原数字需要加 后面组成的数字个数 
    }
    pos++;
}

int main()
{
    int a, b;
    int i, j, k, kk;

    for(i=0; i<=9; i++)
    {
        dp[1][i][i] = 1;
    }

    for(i=2; i<=9; i++)
    {
        for(j=0; j<=9; j++)
        {
            for(k=0; k<=9; k++)
            {
                if(j==k){
                    dp[i][j][k] = work(i-1);
                }
                for(kk=0; kk<=9; kk++)
                {
                    dp[i][j][k] += dp[i-1][kk][k];  
                }   
            }   
        }   
    }   

    while(1)
    {
        cin >> a >> b;
        pos = 0;
        memset(ans, 0, sizeof(ans));
        if(a == 0 && b == 0){
            break;
        }
        if(a > b){
            int t = a;
            a = b;
            b = t;
        }

        solve(b+1);
        solve(a);

        for(i=0; i<=9; i++)
        {
            cout << ans[i][0] - ans[i][1] << " ";
        }
        cout << endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值