wd
感觉题目的想法还是挺不错的.
很明显是数位dp的题, 但是一开始没想出来, 因为之前做都是一个数满足什么什么条件, 这里是一对数满足什么什么条件.
还是想一下怎么确定状态.
- 当前位.
- 位数和的差值. 这个状态很重要, 就是我们不需要知道前面具体选择了两个数的具体值, 只要知道差值就好了
- 前面选择的位是否相等. 这个是必要的, 因为位数和的差值为0并不代表前面选择的都是相同的.
这三个就可以确定完状态了.
然后每次状态转移的时候. 如果前面选择的位都是相同的, 那么此时这位, 第二个数只能选择小于等于第一个数的.
如果前面第二个数已经小于第一个数了, 那第二个数就可以随便选了. 就和字典序是一样的.
细节
有两个细节问题一开始没处理好.
位数和的差值.
这个差值是会为负的, 所以要把零点设置到1000
limit的状态
以前写数位dp都没把高位限制放到状态里, 也能过. 但是这题就T了. 所以要把高位限制放到状态里
时间复杂度
就是dp的状态数. 因为最多到100位, 所以位数和的差值最大是
. 正负还要乘2.
状态数就是
.
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
const int mod = 1e9 + 7;
char s[N];
int digit[N];
int dp[N][2005][2][2];
void add(int &a, int b)
{
a += b;
if (a >= mod)
a -= mod;
}
int t;
int dfs(int len, bool limit, int gap, bool equal)
{
if (dp[len][gap][(int)equal][limit] != -1)
return dp[len][gap][(int)equal][limit];
if (len == 0)
return gap < 1000;
int up = limit ? digit[len] : 9;
int ans = 0;
for (int i = 0; i <= up; i++)
{
if (equal)
{
for (int j = 0; j < i; j++)
add(ans, dfs(len - 1, limit && (i == up), gap + i - j, false));
add(ans, dfs(len - 1, limit && (up == i), gap, true));
}
else
{
for (int j = 0; j <= 9; j++)
add(ans, dfs(len - 1, limit && (i == up), gap + i - j, false));
}
}
dp[len][gap][(int)equal][limit] = ans;
return ans;
}
int main()
{
memset(dp, -1, sizeof(dp));
scanf("%s", s);
int l = strlen(s);
for (int i = 0; i < l; i++)
{
digit[i + 1] = s[l - i - 1] - '0';
}
cout << dfs(l, 1, 1000, 1) << endl;
return 0;
}```



京公网安备 11010502036488号