把落点压成「本行只有一格」和「本行已横走成段」两类,按 3 种/2 种续走做线性递推。
OJ: roj
题目 ID: 1196
难度:普及-
标签:递推动态规划计数python
创建: 2026-09-29 23:04
更新: 2026-10-05 05:18
题目描述
在无穷大的方格矩阵上从某点出发走
输入一个整数 2,输出 7。
思路
没有向南的走法,落点所在的最高一行只能被横走成一段连续区间,且落点是这段区间的端点。把落点压成两类:本行只有落点一格(可走北/东/西共 3 种)、本行已横走成段(只剩向北 + 唯一一侧横走共 2 种),线性递推
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-29 23:04
# update_at: 2026-09-29 23:24
import sys
def solve() -> None:
"""走 n 步且不重复踩格的方案数:按当前端点的「本行是否横走过」分两类计数。"""
n = int(sys.stdin.buffer.read())
# 终点(最后一步的落点)总在最高的一行上,而这一行踩过的格子必是连续一段。
# fresh:终点所在行只有它自己,向北/东/西都踩新格子(3 种走法)。
# walked:终点所在行已有别的格子,终点是区间端点,只剩向北 + 唯一一侧的横走(2 种)。
fresh, walked = 1, 0
for _ in range(n):
# 向北都只踏入全新的一行(两类都变 fresh);横向走法数才是差别:
# fresh 有 2 种、walked 只剩 1 种,都会让本行多踩一格(变 walked)。
fresh, walked = fresh + walked, 2 * fresh + walked
print(fresh + walked)
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 05:18
* update_at: 2026-10-05 05:18
*/
#include <cstdio>
typedef long long ll;
// 终点(最后一步的落点)总在最高的一行上,且该行被踩的格子是一段连续区间,落点是区间端点。
// fresh :本行只有落点这一格,向北/东/西都是新格子,共 3 种走法;
// walked :本行已横走过一段,落点是端点,只剩向北 + 唯一一侧横走,共 2 种走法。
ll fresh; // 走 k 步后落点在「本行只有一格」状态的方案数
ll walked; // 走 k 步后落点在「本行已横走成一段」状态的方案数
int main() {
ll n; // 允许行走的步数,n <= 20
scanf("%lld", &n);
// 初值:还没走时只有起点,本行只有它自己
fresh = 1;
walked = 0;
for (ll i = 1; i <= n; i++) {
// 向北都踏入全新一行(两类都变 fresh);fresh 有 2 种横走、walked 只剩 1 种,横走都变 walked
ll nf = fresh + walked; // 3 种走法里 1 种向北变 fresh:a' = a + b
ll nw = 2 * fresh + walked; // 2 种横走变 walked:b' = 2a + b
fresh = nf;
walked = nw;
}
printf("%lld\n", fresh + walked);
return 0;
}