题目链接
题目描述
给定一个长度100位的数字,统计有几个数满足
A
<
B
A<B
A<B且
A
A
A的位数之和大于
B
B
B的位数之和。
思路
记忆化搜索,挺暴力的数位dp。
d
p
[
i
]
[
j
]
[
k
]
[
l
]
[
m
]
dp[i][j][k][l][m]
dp[i][j][k][l][m]表示到第
i
i
i位数字时,
A
A
A和
B
B
B的差值为
j
j
j,且数字
A
A
A是否为上限最大值,
k
=
1
k=1
k=1表示该位数字为
A
A
A的上限最值,
l
=
1
l=1
l=1表示该位数字为
B
B
B的上限最值,
m
=
1
m=1
m=1表示
B
B
B高位大于
A
A
A,也就是说当前状态下
B
B
B必大于
A
A
A的状态。
因为要考虑负数的状态,所以初始值为
1000
1000
1000,每一位数字为
9
9
9最多的和也只有
900
900
900.
上述的
i
,
j
,
k
,
l
,
m
i,j,k,l,m
i,j,k,l,m也对应
d
f
s
dfs
dfs中的
p
o
s
,
s
u
m
,
l
i
m
a
,
l
i
m
b
,
f
l
a
g
pos,sum,lima,limb,flag
pos,sum,lima,limb,flag。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int p = 1e9 + 7;
int dp[105][2010][2][2][2];
string str;
int len;
LL dfs(int pos, int sum, bool lima, bool limb, bool flag) {
if(pos == len) {
return sum > 1000;
}
if(dp[pos][sum][lima][limb][flag] != -1) return dp[pos][sum][lima][limb][flag];
int a = lima ? str[pos] - '0' : 9;
int b = limb ? str[pos] - '0' : 9;
LL res = 0;
for(int i = 0; i <= a; i++) {
for(int j = 0; j <= b; j++) {
if(!flag && i > j) continue;
res = (res + dfs(pos + 1, sum + i - j, lima && i == a, limb && j == b, flag || j > i)) % p;
}
}
return dp[pos][sum][lima][limb][flag] = res;
}
void solve() {
memset(dp, -1, sizeof dp);
cin >> str;
len = str.size();
printf("%d\n", dfs(0, 1000, 1, 1, 0));
}
int main() {
// freopen("in.txt", "r", stdin);
solve();
return 0;
}