user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
没有连续三位数字的和超过给定值的数

有多少个20位数字n(不包括前导0)满足,不存在连续三位数字的和超过9

我的思路:dp.

设dp[l][i][j][k]为长度为l的,当前所填位数为i,上一位为j,上上一位为k的所有数的情况.
直接转移即可.
初始情况:
dp[1][0~9][0][0]=1
答案:
dp[20][1~9][all][all]

请给我c++代码实现,你可能需要int128

gpt

1
2
3
4
5
6
7
8
9
你的思路是**完全正确的**,而且这是一个非常标准的「带历史状态的数位 DP」。

下面我给你一份**干净、可直接编译的 C++ 实现**,使用 `__int128` 存储答案,并且逻辑上与你的 DP 定义**等价但稍微简化**,方便实现。

---

## 状态设计(等价但更清晰)

我们定义:
1
dp[pos][last][last2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
含义是:

* 已经填了 `pos` 位
* 最近一位是 `last`
* 倒数第二位是 `last2`
* 满足「任意连续三位数字之和 ≤ 9」

`pos ∈ [1, 20]`
`last, last2 ∈ [0, 9]`

### 关键点

* **前导 0 不允许** → 第 1 位只能是 `1~9`
* 当 `pos < 3` 时,不检查三位和
* 当 `pos ≥ 3` 时,要求 `new + last + last2 ≤ 9`

---

## C++ 实现(使用 __int128)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <bits/stdc++.h>
using namespace std;

using i128 = __int128_t;

i128 dp[21][10][10];

int main() {
// 初始化:第 1 位,不能是 0
for (int d = 1; d <= 9; d++) {
dp[1][d][0] = 1;
}

for (int pos = 1; pos < 20; pos++) {
for (int last = 0; last <= 9; last++) {
for (int last2 = 0; last2 <= 9; last2++) {
i128 cur = dp[pos][last][last2];
if (cur == 0) continue;

for (int d = 0; d <= 9; d++) {
if (pos >= 2 && d + last + last2 > 9) continue;
dp[pos + 1][d][last] += cur;
}
}
}
}

// 统计答案
i128 ans = 0;
for (int last = 0; last <= 9; last++) {
for (int last2 = 0; last2 <= 9; last2++) {
ans += dp[20][last][last2];
}
}

// 输出 __int128
string s;
while (ans > 0) {
s.push_back('0' + ans % 10);
ans /= 10;
}
if (s.empty()) s = "0";
reverse(s.begin(), s.end());
cout << s << '\n';

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---

## 复杂度分析

* 状态数:`20 × 10 × 10 = 2000`
* 转移:每个状态尝试 10 个数字
* 时间复杂度:**O(20 × 10³)**,极小
* 空间复杂度:**O(20 × 10²)**

---

## 小结

✔ DP 设计正确
✔ `__int128` 使用合理
✔ 无前导零处理正确
✔ 连续三位约束精确

如果你愿意,下一步我可以帮你:

* 用 **矩阵快速幂** 做推广(比如 1000 位)
* 改成 **自动机 + DP**
* 或推广为「连续 k 位之和 ≤ S」

你这个题本身已经是竞赛级别思路了 👍

fun fact

gpt好像把最后两位数字压到一起了,因为加和小于9,所以直接用一个9存储也是可以的.

一发过,正常现象.

似乎带点”算法”的题目在欧拉计划的难度都虚高,以后要看过题数决定题目真的难不难.