p2602 数字计数

P2602 [ZJOI2010] 数字计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

AC代码(递推实现):

#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;
typedef long long ll;
const int N = 15;

ll cnta[N], cntb[N];//数字0到9出现的次数
int nums[N];//用数组存一个数的每个位
ll f[N];//f[i]表示长度为i每个数字出现的次数,每个数字出现的次数是相等的
ll ten[N];//10的i次方

void solve(ll x, ll* cnt)
{
	int len = 0;
	while (x)
	{
		nums[++len] = x % 10;
		x /= 10;
	}

	for (int i = len; i >= 1; i--)
	{
		for (int j = 0; j <= 9; j++)cnt[j]+= f[i - 1] * nums[i];//先算出当前位的后面所有数字出现的次数

		for (int j = 0; j < nums[i]; j++)cnt[j] += ten[i - 1];//算出小于当前位的数字出现的次数

		ll num = 0;
		for (int j = i - 1; j >= 1; j--)//计算当前位后面的数字的大小
			num = num * 10 + nums[j];

		cnt[nums[i]] += num + 1;
		cnt[0] -= ten[i - 1];
    }

}

int main()
{
	ll a, b; cin >> a >> b;
    ten[0]=1;
	for (int i = 1; i <= N; i++)
	{
		f[i] = i * pow(10, i) / 10;
		ten[i] = pow(10, i);
	}
	solve(a - 1, cnta);
	solve(b, cntb);
	for (int i = 0; i <= 9; i++)
	{
		cout << cntb[i]-cnta[i]<<" ";
	}
	return 0;
}

相关解释:

这里从大到小枚举位数,可以将O(n*n)的复杂度降为个位数乘以个位数的级别,非常优秀。从大到小枚举位数,假设这里枚举到了abcdefg七位数,先计算后面六位数每个数字出现的次数,然后计算0到a-1的数字的在第七位次数,最后计算后面六位数的大小,再加个一就是当前位的数字的次数。因为当前位置是0的数字我们计算了一下,也就是前导0,0bcdefg这个数字不符合要求,所以应该剪掉。最后利用前缀和的思想输出最终的答案。

AC代码(记忆化搜索实现):

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int N = 20;
int now,nums[N];
ll f[N][N];//f[i][j]表示后面i位,前面now的个数为j的now的总个数,now是要计算的0到9的任意一位


ll dfs(int pos,int sum,bool lead,bool limit)
{
    if(pos==0)return sum;
    ll res=0;
    if(!limit&&!lead&&f[pos][sum]!=-1)return f[pos][sum];
    int up=(limit?nums[pos]:9);
    for(int i=0;i<=up;i++)
    {
        if(i==0&&lead==true)
        res+=dfs(pos-1,sum,true,limit&&i==up);
        else if(i==now)
        res+=dfs(pos-1,sum+1,false,limit&&i==up);
        else
        res+=dfs(pos-1,sum,false,limit&&i==up);
    }
    
    if(!limit&&!lead)f[pos][sum]=res;
    
    return res;
}

ll solve(ll x)
{
    memset(f,-1,sizeof f);
    int len=0;
    while(x)
    {
        nums[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,true,true);
}

int main()
{
    ll a,b;
    cin>>a>>b;
    
    for(int i=0;i<=9;i++)
    {
        now=i;
        cout<<solve(b)-solve(a-1)<<" ";
    }
    return 0;
}

相关解释:

这里的记忆化搜索代码可以当作模板使用来解决计数DP类型的题目。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值