题目链接:数页码 - 洛谷
分析:这道题目与数字计数那道题基本上是一样的,那道题目里面是求出0~9每一位数字出现的次数,而这道题目是求出每个数每位上的数的和,这就可以先求出来每一位出现的次数,再乘以该位对应的权重即可,这道题不需要考虑0了,所以完全就不需要考虑前导0,所以会简单一些,建议大家看一下我写的关于数字计数的那篇博客,里面有一些需要注意的点,详细介绍了每一位出现次数的求解方法,而且介绍了一些常见错误,或许对大家还是有所帮助的。下面附上数字计数博客地址:P2602 [ZJOI2010]数字计数(数位DP)_AC__dream的博客-CSDN博客
鉴于在数字计数那道题目中已经详细介绍了每一位出现次数的求法,本道题目我就直接放上代码;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=15;
typedef long long ll;
ll f[N][N],a[N];
ll dfs(int pos,int cnt,int limit,int t)
{
if(!pos) return cnt;
if(!limit&&f[pos][cnt]!=-1) return f[pos][cnt];
ll ans=0;
int up=limit?a[pos]:9;
for(int i=0;i<=up;i++)
ans+=dfs(pos-1,cnt+(i==t),limit&&(i==up),t);
if(!limit) f[pos][cnt]=ans;
return ans;
}
ll solve(ll x,int t)
{
if(!x) return 0;
memset(f,-1,sizeof f);
int pos=0;
while(x)
{
a[++pos]=x%10;
x/=10;
}
return dfs(pos,0,1,t);
}
int main()
{
ll n,ans=0;
cin>>n;
for(int i=1;i<=9;i++)
ans+=i*solve(n,i);
printf("%lld",ans);
return 0;
}
当然这道题目也可以不用一位一位求,我们可以直接令f[pos][cnt]表示后pos位任选且除了后pos位以外的位上各位数字之和为cnt的贡献,这样我们就可以一次搜索求出最终答案了,但是此时的cnt记录的不再是出现次数,而是出现次数乘以其权值。
下面是代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=15;
typedef long long ll;
ll f[N][N*N],a[N];
ll dfs(int pos,int cnt,int limit)
{
if(!pos) return cnt;
if(!limit&&f[pos][cnt]!=-1) return f[pos][cnt];
ll ans=0;
int up=limit?a[pos]:9;
for(int i=0;i<=up;i++)
ans+=dfs(pos-1,cnt+i,limit&&(i==up));
if(!limit) f[pos][cnt]=ans;
return ans;
}
ll solve(ll x)
{
if(!x) return 0;
memset(f,-1,sizeof f);
int pos=0;
while(x)
{
a[++pos]=x%10;
x/=10;
}
return dfs(pos,0,1);
}
int main()
{
ll n,ans=0;
cin>>n;
printf("%lld",solve(n));
return 0;
}