题解 P7063 【[NWRRC2014]Digits】
Error_Eric · · 题解
前言
首A!
正文
这是一种暴力的解法,如果大家有更好的解法欢迎提出。
思路:枚举 1~很大 之间的所有正整数,统计每一个数位和出现的次数,用 sum 数组维护每一个数位和的数字的和,然后用 ans 变量维护答案。
我的第一版代码是这样的:
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
int f(int x){//计算数位和,复杂度 lg x .
int tot=0;
while(x)tot+=x%10,x/=10;
return tot;
}
int n,u,di=0,cnt[500];//cnt维护每一个数位和的数字出现的次数.
long long sum[500]={-0x7fffffff,0};
long long ans=0x7fffffffffffffll;//题目中已说明
int main(){
scanf("%d",&n);//输入
for(int i=1;i<=600000;i++){//枚举正整数
u=f(i),(cnt[u]<n)?(cnt[u]++,sum[u]+=i):0;//维护
if(cnt[u]==n)ans=min(ans,sum[u]);//如果cnt恰好是 n , 维护 ans
}
printf("%d\n",ans);//输出答案 .
}
时间复杂度
大概是
这个时候,脑海中有一个声音出现了:“我不满意!”
即使是常数复杂度,也可以优化!
优化1
把目光投向 f(x) 。
可以发现,经常有不必要的计算,比如前一次计算了 12345654321234321 后一次就要计算 12345654321234322 (当然没有这么大的,只是举个例子),很浪费时间!
不过,我们可以发现,只有这个数字末尾的数字是
我们可以得到以下优化:
int las,lan;
int f(int x){
if(las%10!=9)return (las=x),++lan;
int tot=0;
while(x)tot+=x%10,x/=10;
return tot;
}
函数复杂度缩减为90%情况
优化2
把目光投向 for(int i=1;i<=600000;i++) 。
可以发现
稍微输出一下最大的数字,可以发现,
可以得到以下优化:
for(int i=1;i<=586775;i++)
优化3
把目光投向 long long ans 。
经测验,不开 long long 是可以的,答案最大是
优化4
经测验,数位和最大
cnt,sum开
最终得出代码:
Code
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
int n,u,cnt[51],las,lan;
int sum[51],ans=193969046;
int f(int x){
if(las%10!=9)return (las=x),++lan;
int tot=0;
while(x)tot+=x%10,x/=10;
return tot;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=586775;i++)
u=f(i),cnt[u]++,sum[u]+=i,(cnt[u]==n)?(ans=min(ans,sum[u])):0;
printf("%d\n",ans);
}
时间复杂度是
大概是 只优化了三分之二。