您的位置:首页 > 其它

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

2017-04-29 10:48 351 查看
对于区间计数问题,一般都是令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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: