uva 1640 两种方法 数位dp或枚举计算

对于区间计数问题,一般都是令F {i}(n)为[0,n]之间i出现的次数,再去区间相减就可以了

第一种方法:

例如 4321,分别求0-3999,4000-4299,4300-4319,4320+4321

0-3999中,最高位只出现过0,1,2,3, 对应的它出现的次数为1000(x***),其他位上每种数字出现的次数相同,都是(1000*3)/10

前导0不算,为了计算方便,先算进去,再减,不算的情况只出现在了第一次计算0-3999的时候,有多少个呢? 

1000+100+10

所以我们按位从高位开始枚举,来统计各个数字出现的次数

注意 在计算10^n的时候不能用pow转换成整形来计算,会出现精度丢失的情况吗,所以自己生成一个就行了。

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <queue>
using namespace std;

const int INF=1e9+1000;
const double EPS = 1e-10;  
typedef long long ll;
int a[15],b[15];
int pova[17];
int mpow10[10];
void cal(int n,int *d){
    int tmp=n;
    int pos=0;
    int c[15];
    do{
        c[pos++]=tmp%10;
        tmp/=10;
      }while(tmp);

    pos--;
    char s[15];
    sprintf(s,"%d",n);
    for(int i=0;i<=pos;i++){
        sscanf(s+pos-i,"%d",&pova[i]);
    }
    int k=mpow10[pos];
    for(int i=pos;i>0;i--){

        for(int j=0;j<c[i];j++){
            d[j]+=k;
            for(int z=0;z<=9;z++){
                d[z]+=k*i/10;
            }
        }
        d[c[i]]+=pova[i-1]+1;
        d[0]-=k;
        k/=10;
        
    }
    
    for(int i=0;i<=c[0];i++)
        d[i]++;
}
int main(){
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(false);
    int l,r;
    mpow10[0]=1;
    for(int i=1;i<=8;i++)
        mpow10[i]=mpow10[i-1]*10;
    while(scanf("%d %d",&l,&r)&&l+r){
        if(l>r) swap(l,r);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        cal(l-1,a);
        cal(r,b);
        for(int i=0;i<=9;i++)
            printf("%d%c",b[i]-a[i],i==9?'\n':' ');
    }
    return 0;
}


第二种方法,数位dp,就是记忆化搜索,对于重复计算的状态,存在数组中就可以直接返回了。

其中  cnt的设置很是精妙,避免了一大堆的判定,lead用来判断枚举到该位的时候是否有前导0,。

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <queue>
using namespace std;

const int INF=1e9+1000;
const double EPS = 1e-10;  
typedef long long ll;
int a[15],b[15];
int digit[15];
int d[15][2][15][15];

int dfs(int pos,int lead,int val,int limit,int cnt){
	if(pos==-1){
		if(lead)
			return 1;
		else
			return cnt;
	}
	if(!limit&&d[pos][lead][val][cnt]!=-1) return d[pos][lead][val][cnt];
	int up=limit?digit[pos]:9;
	int ans=0;
	for(int i=0;i<=up;i++){
		if(!lead){
			ans+=dfs(pos-1,0,val,limit&&i==up,cnt+(i==val));
		}else{
			if(i==0){
				ans+=dfs(pos-1,1,val,limit&&i==up,cnt);
			}
			else{
				ans+=dfs(pos-1,0,val,limit&&i==up,cnt+(i==val));
			}
		}
	}
	if(!limit) d[pos][lead][val][cnt]=ans;
	return ans;
}

void cal(int n,int *f){
	int pos=0;
	do{
		digit[pos++]=n%10;
		n/=10;
	}while(n);
	for(int i=0;i<=9;i++){
		f[i]=dfs(pos-1,1,i,1,0);
	}
}
int main(){
	//freopen("out.txt","w",stdout);
	//ios_base::sync_with_stdio(false);
	int l,r;
	memset(d,-1,sizeof(d));
	while(scanf("%d %d",&l,&r)&&l+r){
		if(l>r) swap(l,r);
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		cal(l-1,a);
		cal(r,b);
		for(int i=0;i<=9;i++)
			printf("%d%c",b[i]-a[i],i==9?'\n':' ');
	}
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值