Harmony Pairs
题目描述:
设
S
(
x
)
S(x)
S(x)表示十进制表示下
x
x
x的每位数字之和,当
S
(
A
)
S(A)
S(A)
>
>
>
S
(
B
)
S(B)
S(B)时
(
A
,
B
)
(A,B)
(A,B)表示一个和谐对。
给定一个数
N
N
N,求满足
0
≤
A
≤
B
≤
N
0≤A≤B≤N
0≤A≤B≤N的和谐对
(
A
,
B
)
(A,B)
(A,B)的数量,答案对
1
0
9
+
7
10^9+7
109+7取模。
输入描述:
输入只有一行,表示一个整数 N N N ( ( ( 1 1 1 ≤ ≤ ≤ N N N ≤ ≤ ≤ 1 0 100 10^{100} 10100 ) ) )
输出描述:
输出一个整数,表示答案。
样例输入:
100
样例输出:
967
思路:
一看到这个
N
N
N的取值范围,又看到和数字有关,那不就是数位
d
p
dp
dp嘛…
首先,我们回想数位
d
p
dp
dp的做法
:
:
:暴力枚举,使枚举方式满足
d
p
dp
dp数组各维度的性质,最后再记忆化。
数位DP详解
接下来我们就可以看这道题目了,我们确定
d
p
dp
dp数组的第一个维度为
p
o
s
pos
pos,表示当前所在的位置。
然后我们很容易想到第二个和第三个维度分别表示
A
A
A前面的位数和和
B
B
B前面的位数和,第四个和第五个维度分别表示
A
A
A的高位是否与
B
B
B的相同,
B
B
B的高位是否与
N
N
N相同,那么问题来了,如果这样开数组,那么
d
p
dp
dp数组就是这个样子的:
d
p
[
100
]
[
900
]
[
900
]
[
2
]
[
2
]
dp[100][900][900][2][2]
dp[100][900][900][2][2]
可这明显开不下呀!
这时我们就想第二第三个维度是否可以简化?答案是肯定的!我们将二三两个维度压缩成一个维度,表示
A
A
A前面的位数和
−
-
−
B
B
B前面的位数和,那么
d
p
dp
dp数组就可以变为:
d
p
[
100
]
[
2000
]
[
2
]
[
2
]
dp[100][2000][2][2]
dp[100][2000][2][2]
解决了
d
p
dp
dp数组的问题,接下来就好办了,具体做法详见代码。
AC Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=105;
const int mod=1e9+7;
int dp[MAXN][MAXN*20][2][2],dig[MAXN],len;
char s[MAXN];
ll dfs(int pos,int sum,int lit1,int lit2)
{
if(!pos) return sum>1000;//1000的偏移量可以保证差值非负
if(~dp[pos][sum][lit1][lit2]) return dp[pos][sum][lit1][lit2];//如果已经搜过了就直接返回值
ll ret=0;
for(int i=0;i<=(lit1?dig[pos]:9);i++)//限制条件B<=N
for(int j=0;j<=(lit2?i:9);j++)//限制条件A<=B
ret=(ret+dfs(pos-1,sum+j-i,lit1&i==dig[pos],lit2&i==j))%mod;
return dp[pos][sum][lit1][lit2]=ret;//记忆化
}
int main()
{
memset(dp,-1,sizeof(dp));
scanf("%s",s);
len=strlen(s);
for(int i=0;i<len;i++)
dig[len-i]=s[i]-'0';
printf("%lld\n",dfs(len,1000,1,1)%mod);
}