33,009
社区成员
发帖
与我相关
我的任务
分享
public class Count1 {
public long count(long n){
long num=0;
num=(int)Math.ceil(n/10)+1; //获取从1到n之间出现在个位上的1的个数
//计算从十位后每一位上1的个数
for(double i=2d;i<=Long.toString(n).length();i++){
num=num+countWei(n,i) ;
}
return num;
}
/**
* 求1到n之间某位上1的个数
* 如:十位上1的个数就等于n/100取整后乘以10 + ((余数部分-10)大于10那么返回10否则就返回(余数部分-10))
* 百位上1的个数就等于n/1000取整后乘以100 + ((余数部分-100)大于100那么返回100否则就返回(余数部分-100))
* ……
* 以此类推
* X位上1的个数就等于n/X*10取整后乘以X + ((余数部分-X)大于X那么返回X否则就返回(余数部分-X+1))
*
* @param n 最大数(同上)
* @param d 要计算1的个数的某位(如:(10)十位d=2 ,(100)百位d=3……x位d等于x开10次方加1)
* @return
*/
public long countWei(long n,double d){
long num=0;
long weishu= Double.valueOf(Math.pow(10d, d)).longValue();//
num=num+(n/weishu)*(weishu/10);
if((n%weishu-weishu/10)>=(weishu/10)){
num=num+weishu/10;
}else{
num=num+(n%weishu-weishu/10)+1;
};
return num;
}
public static void main(String args[]){
Count1 c=new Count1();
long l=92233720368547l;
System.out.println(c.count(l));
}
}
/********************************************************************
purpose:
在从1到n的正数中1出现的次数
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。
求满足f(i)=i(i<=911111111099999009)这样的数总计有多少个
*********************************************************************/
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
int len;
int *perDigit; // 每位上的数字
long long* fullOne; // 位数是n位的数,含1的总个数为fullOne[n]
long long* addTopOne; // 位数为n的数,首位为1时有多少个这样的数addTopOne[n] 即10^(n-1)
int curDigit;
long long cnt = 0;
long long fixBit1 = 0;
int ocuur1Cnt = 0;
long long getOneCnt(long long n)
{
len = 0;
while (n > 0) { // 把数字n分成十进制串对应的数字数组
perDigit[len] = n % 10;
n = n / 10;
len++;
}
cnt = 0;
fixBit1 = 0; // 数字n中1所在的位被小于此位的数字重复的次数
ocuur1Cnt = 0; // 数字n有几位是1
for (int i = len - 1; i >= 0; i--) { // 从十进制数高位到低位
curDigit = perDigit[i];
if (ocuur1Cnt > 0) {
fixBit1 = fixBit1 * 10 + ocuur1Cnt * curDigit;
}
if (curDigit > 0) {
if (curDigit == 1) {
cnt += curDigit * fullOne[i];
ocuur1Cnt++;
} else {
cnt += curDigit * fullOne[i] + addTopOne[i];
}
}
}
return cnt + fixBit1 + ocuur1Cnt;
}
void HowManyOne(long long topNum)
{
clock_t start, finish; // 记录计算开始结束时间
start = clock();
len = 20; // 最长20位十进制数
perDigit = new int[len];
fullOne = new long long[len];
addTopOne = new long long[len];
fullOne[0] = 0;
addTopOne[0] = 1;
cnt = 1;
for (int i = 1; i < len; i++) { // 初始化信息
fullOne[i] = fullOne[i-1]*10 + cnt;
cnt *= 10;
addTopOne[i] = cnt;
}
long long stack[1000]; // 存储数据段, [from, to]及计算方向,每次分别存入from,to,dir
long long lRel[1000]; // 符合f(i)==i表达式的i的数组
int pStack = 0, pRel = 0; // stack与lRel当前长度或下一次存储位置或栈顶
long long from, to, dir; // 当前要验证的一段数据的始终与验证方向,验证方向为0x01(向上) 0x10(向下) 0x11(向上向下均可以)
long long fn, dist; // fn当前一个数字n对应的1到n所有数字的十进制中的1的总个数;dist临时变量(from与to的差)
stack[0] = 1;
stack[1] = topNum;
stack[2] = 3; // 0x11
pStack = 3;
int maxP = 0;
while (pStack > 0) { // 从stack中取出一段数据,验证其中的i是否满足f(i)==i
dir = stack[--pStack];
to = stack[--pStack];
from = stack[--pStack];
if ((dir & 0x01) != 0) { // UP 从from开始向to的方向计算 f(i)==i
while (from <= to) {
fn = getOneCnt(from);
if (fn > from) {
from = fn;
} else if (fn < from) {
from++;
break;
} else {
lRel[pRel++] = fn;
from++;
}
}
}
if ((dir & 0x10) != 0) { // down 从to开始向from的方向计算 f(i)==i
while(from <= to) {
fn = getOneCnt(to);
if (fn < to) {
to = fn;
} else if (fn > to) {
to--;
break;
} else {
lRel[pRel++] = fn;
to--;
}
}
}
if (to-from<2) { // 这一段己经很小,直接计算完
for (;from<=to;from++) {
if (from == getOneCnt(from)) {
lRel[pRel++] = fn;
}
}
} else { // 当前段向上向下己计算完,二分后入栈,再计算
dist = (to - from) >> 1;
dist = from + dist;
stack[pStack++] = from;
stack[pStack++] = dist;
stack[pStack++] = 0x10;
stack[pStack++] = dist+1;
stack[pStack++] = to;
stack[pStack++] = 0x01;
}
}
finish = clock();
int i = pRel;
while(i > 0) // 输出符合f(i)==i的所有i
cout<<lRel[--i] <<endl;
cout<<"time: " << ((double)(finish-start))<<"ms" << endl;
cout<<"total: " <<pRel<<endl;
}
void Test_HowManyOne()
{
HowManyOne(911111111099999009LL);
}