51nod1009

给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数。
 
例如:n = 12,包含了5个1。1,10,12共包含3个1,11包含2个1,总共5个1。
Input
输入N(1 <= N <= 10^9)
Output
输出包含1的个数
Input示例
12
Output示例
5
思路:一看到这道题就想到了记忆化搜索,奈何太久没打过了,忘记了太多要素,因此被此题卡了四个多小时。。。。。吐血啊
#include<stdio.h>
#include<string.h>
int dis[12];
int lg,len;
int s[12];
int dp[12][2];
int check(int a){
	int i=0;
	if(a<0)
	return 0;
	int ans=0;
	for(i=0;i<=a;i++)
	ans+=dis[i]*s[i];
	//printf("%d %d\n",a,ans);
	return ans;
}
int dfs(int pos,int lg){
	if(pos<0)
	return 0;
	int num=lg?dis[pos]:9;
//	printf("num=%d\n",num);
	if(!lg&&dp[pos][lg]!=-1)
	return dp[pos][lg];
	
	int i,j;
	int ans=0;
	
	for(i=0;i<=num;i++){//计算当第pos位为i时时,后面pos-1位有多少种情况; (当为i时,计算的范围为i*10^(pos)----(i+1)*10^(pos)-1) 
		if(i==1){
			if(lg&&i==num)
			{ans=ans+check(pos-1)+1+dfs(pos-1,lg&&(i==num));//0---check(pos-1),所以要加一 
				
				//printf("a%d %d %d\n",pos,i,ans);
			}
			else
			{
				ans=ans+s[pos]+dfs(pos-1,lg&&(i==num));//当此位为1时,它没有被限制,那么可以分解成10000+(0---9999)(假设当前有五位) 
				//printf("b%d\n",ans);//0---9999的每一个数都可以在前面加一个 。 
			}
		}
		else
		{ans+=dfs(pos-1,lg&&(i==num));
	//	printf("c%d\n",ans);
		}
	}
//	printf("%d %d\n",pos,ans);
	if(!lg)
	dp[pos][lg]=ans;
	return ans;
}
int main(){
	int n;
	len=0;
	scanf("%d",&n);
	int i;
	s[0]=1;
	for(i=1;i<=9;i++)
	s[i]=s[i-1]*10;
	while(n){
		dis[len++]=n%10;
		n=n/10;
	}
	memset(dp,-1,sizeof(dp));
	printf("%d\n",dfs(len-1,1));
	return 0;
}

  

posted @ 2018-07-14 14:03  cglong  阅读(307)  评论(0编辑  收藏  举报