「
「
「动态规划
」
」
」第
3
3
3章 数位
D
P
(
DP(
DP(前
3
3
3题
)
)
)
目录:
A.B数计数
B.区间圆数
C.数字计数
A . A. A. 例题 1 1 1 B B B数计数
分析:
p
o
s
pos
pos表示位数
m
o
d
mod
mod表示膜的余数
k
k
k表示出现
13
13
13的状态
k
=
0
k=0
k=0 表示没有
k
=
1
k=1
k=1 表示没有出现
13
13
13 但最高位为
3
3
3 这时找到
1
1
1即可
k
=
2
k=2
k=2 表示出现
13
13
13
f
p
o
s
,
m
o
d
,
k
f_{pos,mod,k}
fpos,mod,k为符合条件的数的数量
然后就试填法
s
s
s为当前
p
o
s
pos
pos位上的数 要填只能填
<
s
<s
<s的
这样只能模拟到
n
−
1
n-1
n−1 所以最后特判
n
n
n或把
n
+
1
n+1
n+1
CODE:
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=15;
int f[N][N][4],power[N],n;
void DP()
{
f[0][0][0]=1;
for(int pos=1;pos<=10;pos++)
for(int i=0;i<=9;i++)
for(int mod=0;mod<13;mod++)
{
int mod2=(mod+13-i*power[pos-1]%13)%13;
if(i!=1&&i!=3) f[pos][mod][0]+=f[pos-1][mod2][1];
if(i!=3) f[pos][mod][0]+=f[pos-1][mod2][0];
if(i==3) f[pos][mod][1]+=f[pos-1][mod2][0]+f[pos-1][mod2][1];
if(i==1) f[pos][mod][2]+=f[pos-1][mod2][1];
f[pos][mod][2]+=f[pos-1][mod2][2];
}
}
int query(int x)
{
int k=0,mod=0,ans=0;
for(int pos=10;pos>=1;pos--)
{
int s=x/power[pos-1];
for(int i=s-1;i>=0;i--) //s以下填数
{
int k2,mod2=(mod+i*power[pos-1])%13,mod3=(13-mod2)%13;
if(k==2||(i==3&&k==1)) k2=2;
else if(i==1) k2=1;
else k2=0;
ans+=f[pos-1][mod3][2];
if(k2==2)
ans+=f[pos-1][mod3][0]+f[pos-1][mod3][1];
if(k2==1)
ans+=f[pos-1][mod3][1];
}
if(k!=2)
{
if(s==3&&k==1) k=2;
else if(s==1) k=1;
else k=0;
}
mod=(mod+s*power[pos-1])%13;
x%=power[pos-1];
}
return ans;
}
int main()
{
power[0]=1;
for(int i=1;i<=9;i++)
power[i]=power[i-1]*10;
DP();
while(scanf("%d",&n)!=EOF){
printf("%d\n",query(n+1));
}
return 0;
}
B . B. B. 例题 2 2 2 区间圆数
分析:
[
l
,
r
]
[l,r]
[l,r]的个数 就用
[
1
,
r
]
[1,r]
[1,r]的个数减
[
1
,
l
−
1
]
[1,l-1]
[1,l−1]的个数
f
p
o
s
,
i
f_{pos,i}
fpos,i表示二进制下填到第
p
o
s
pos
pos位 此时这个二进制数有
i
i
i个
0
0
0
(
(
(包含前导
0
)
0)
0)
那么有
f
p
o
s
,
i
=
f
p
o
s
−
1
,
i
−
1
+
f
p
o
s
−
1
,
i
f_{pos,i}=f_{pos-1,i-1}+f_{pos-1,i}
fpos,i=fpos−1,i−1+fpos−1,i
注意前导
0
0
0会对结果影响 就要特判
CODE:
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
int l,r,f[35][35];
void DP()
{
f[0][0]=1;
for(int pos=1;pos<=31;pos++)
{
f[pos][0]=1;
for(int i=1;i<=pos;i++)
f[pos][i]=f[pos-1][i-1]+f[pos-1][i];
}
}
int query(int x)
{
if(x==0) return 0;
int tot=0,s0=0,s1=0,ans=0,s[35];
while(x){
s[++tot]=x&1;
x>>=1;
}
for(int pos=tot;pos>=1;pos--)
{
if(s[pos]&&pos^tot) //判首位
{
s0++;
for(int i=0;i<pos;i++)
if(i+s0>=tot-s0-i)
ans+=f[pos-1][i];
s0--;
}
if(pos^tot)
for(int i=0;i<pos;i++)
if(i>=pos-i) ans+=f[pos-1][i];
s0+=!s[pos];
s1+=s[pos];
}
if(s0>=s1) ans++;
return ans;
}
int main()
{
DP();
scanf("%d%d",&l,&r);
printf("%d",query(r)-query(l-1));
return 0;
}
C . C. C. 例题 3 3 3 数字计数
分析:
f
i
f_i
fi表示在
i
i
i位数字下 每个数有多少个
那
f
i
=
f
i
−
1
×
10
+
1
0
i
−
1
f_i=f_{i-1}\times 10+10^{i-1}
fi=fi−1×10+10i−1 设数字
E
f
g
h
Efgh
Efgh 即首位为
E
E
E
如果只有
E
000
E000
E000 那个数就是后
3
3
3位个数
E
×
f
3
E\times f_3
E×f3 再加上首位个数
1
0
3
10^3
103
再考虑 如果是
E
f
g
h
Efgh
Efgh 要怎么做
其实也可以看成 首位为
f
f
f 首位为
g
g
g 首位为
h
h
h 这时与前面的
E
E
E没有关系
这样就把数每一位拆出来 做上面的过程
关于
0
0
0 要处理前导
0
0
0 共出现
10
×
(
i
−
1
)
+
10
×
(
i
−
2
)
+
.
.
.
+
10
10\times (i-1)+10\times (i-2)+...+10
10×(i−1)+10×(i−2)+...+10次
0
0
0的统计要减去这个值
[
l
,
r
]
[l,r]
[l,r]的个数 就是
[
1
,
r
]
−
[
1
,
l
−
1
]
[1,r]-[1,l-1]
[1,r]−[1,l−1]
CODE:
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=25;
ll l,r,power[N],f[N],Cntl[N],Cntr[N];
void DP(ll x,ll ans[])
{
ll num[N];
int tot=0;
while(x)
{
num[++tot]=x%10;
x/=10;
}
for(int pos=tot;pos>=1;pos--)
{
for(int i=0;i<=9;i++)
ans[i]+=f[pos-1]*num[pos];
for(int i=0;i<num[pos];i++)
ans[i]+=power[pos-1];
ll num2=0;
for(int i=pos-1;i>=1;i--)
num2=num2*10+num[i];
ans[num[pos]]+=num2+1;
ans[0]-=power[pos-1];
}
}
int main()
{
scanf("%lld%lld",&l,&r);
power[0]=1;
for(int i=1;i<=15;i++)
{
f[i]=f[i-1]*10+power[i-1];
power[i]=power[i-1]*10;
}
DP(l-1,Cntl);
DP(r,Cntr);
for(int i=0;i<=9;i++)
printf("%lld ",Cntr[i]-Cntl[i]);
return 0;
}