欢迎光临
我们一直在努力

UVa 986 How Many?

题目描述

考虑平面整数格 Z×ZZ \\times ZZ×Z 中的路径,这些路径仅由步骤 (1,1)(1,1)(1,1) 和 (1,−1)(1,-1)(1,−1) 组成,并且从不经过 xxx 轴下方。高度为 kkk 的峰值定义为路径上纵坐标为 y=ky = ky=k 的点,该点前面紧接一个 (1,1)(1,1)(1,1) 步骤,后面紧接一个 (1,−1)(1,-1)(1,−1) 步骤。问题要求计算从 (0,0)(0,0)(0,0) 出发、终止于 (2n,0)(2n,0)(2n,0) 且恰好有 rrr 个高度为 kkk 的峰值的允许路径数量。

输入格式

输入文件包含多个测试用例,每个测试用例由一行组成,包含自然数 nnn、rrr 和 kkk,分别定义问题(第一个数字为 nnn,第二个数字为 rrr,最后一个为 kkk)。假设 1≤n<201 \\le n < 201≤n<20,0≤r<200 \\le r < 200≤r<20,1≤k<201 \\le k < 201≤k<20。

输出格式

对于每个测试用例,输出一个单独的整数,表示问题的答案,保证结果小于 2312^{31}231。

样例输入

3 1 2
10 3 2

样例输出

2
2002

题目分析

本题要求统计满足特定条件的格路径数量。路径从 (0,0)(0,0)(0,0) 开始,到 (2n,0)(2n,0)(2n,0) 结束,只使用 (1,1)(1,1)(1,1) 和 (1,−1)(1,-1)(1,−1) 两种步骤,并且始终保持在 xxx 轴上方或 xxx 轴上。这类路径被称为 Dyck 路径的变体,因为步长不是 (1,1)(1,1)(1,1) 和 (1,−1)(1,-1)(1,−1) 的典型形式,但通过坐标变换可以转化为标准形式。

关键约束是路径中恰好有 rrr 个高度为 kkk 的峰值。峰值定义为路径上的一个点,其纵坐标为 kkk,且该点之前是上升步骤 (1,1)(1,1)(1,1),之后是下降步骤 (1,−1)(1,-1)(1,−1)。注意,路径的起点和终点也可能成为峰值,但需满足相应条件。

由于 n<20n < 20n<20,路径的总步数为 2n2n2n,最大为 383838。状态空间包括当前位置、剩余步数、已形成的峰值数量以及最后一步的方向。可以使用动态规划或记忆化搜索来统计路径数量。

解题思路

定义状态 dfs(x,y,r,from)\\textit{dfs}(x, y, r, \\textit{from})dfs(x,y,r,from),其中 xxx 表示剩余需要走的步数(从 2n2n2n 开始递减),yyy 表示当前纵坐标,rrr 表示还需要形成的峰值数量,from\\textit{from}from 表示到达当前点的最后一步方向(LEFT_UP\\texttt{LEFT\\_UP}LEFT_UP 表示上一步是 (1,1)(1,1)(1,1),LEFT_DOWN\\texttt{LEFT\\_DOWN}LEFT_DOWN 表示上一步是 (1,−1)(1,-1)(1,−1))。初始状态为 dfs(2n,0,r,LEFT_UP)\\textit{dfs}(2n, 0, r, \\texttt{LEFT\\_UP})dfs(2n,0,r,LEFT_UP),表示从 (0,0)(0,0)(0,0) 出发,剩余 2n2n2n 步,需要 rrr 个峰值,且初始方向视为 LEFT_UP\\texttt{LEFT\\_UP}LEFT_UP 以便处理第一个点可能成为峰值的情况。

在每一步,根据当前方向 from\\textit{from}from 决定可选的下一步:

  • 如果 from=LEFT_UP\\textit{from} = \\texttt{LEFT\\_UP}from=LEFT_UP,表示上一步是上升步骤。此时可以选择继续上升 (1,1)(1,1)(1,1) 或下降 (1,−1)(1,-1)(1,−1)。如果选择下降,并且当前纵坐标 y+1y+1y+1 等于 kkk,则形成一个高度为 kkk 的峰值,需要将剩余峰值数 rrr 减 111。
  • 如果 from=LEFT_DOWN\\textit{from} = \\texttt{LEFT\\_DOWN}from=LEFT_DOWN,表示上一步是下降步骤。此时只能选择上升 (1,1)(1,1)(1,1),因为下降会导致路径穿过 xxx 轴下方(除非当前纵坐标大于 000,但根据问题约束,路径不能低于 xxx 轴,因此下降步骤后必须上升)。

边界条件:

  • 如果 r>x/2r > x / 2r>x/2,则剩余步数不足以形成所需的峰值数量,返回 000。
  • 如果 y≥2ny \\ge 2ny≥2n 或 y<0y < 0y<0,超出合理范围,返回 000。
  • 当 x=0x = 0x=0 时,检查是否满足 y=0y = 0y=0、r=0r = 0r=0 且 from=LEFT_UP\\textit{from} = \\texttt{LEFT\\_UP}from=LEFT_UP,若满足则返回 111,否则返回 000。

使用记忆化数组 dp[x][y][r][from]\\textit{dp}[x][y][r][\\textit{from}]dp[x][y][r][from] 存储已计算的状态,避免重复计算。由于 n<20n < 20n<20,状态数最多为 40×40×20×2=6400040 \\times 40 \\times 20 \\times 2 = 6400040×40×20×2=64000,计算量很小。

代码实现

// How Many
// UVa ID: 986
// Verdict: Accepted
// Submission Date: 2019-12-08
// UVa Run Time: 0.000s
//
// 版权所有(C)2019,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>

using namespace std;

const int LEFT_UP = 0, LEFT_DOWN = 1;

int dp[64][64][64][2], N, R, K;

int dfs(int x, int y, int r, int from)
{
if (r > x / 2) return 0;
if (y >= 2 * N || y < 0) return 0;
if (x == 0) return y == 0 && r == 0 && from == LEFT_UP;
if (~dp[x][y][r][from]) return dp[x][y][r][from];
int ret = 0;
if (from == LEFT_UP)
{
if (y + 1 == K) ret += dfs(x – 1, y + 1, r – 1, LEFT_DOWN);
else ret += dfs(x – 1, y + 1, r, LEFT_DOWN);
ret += dfs(x – 1, y + 1, r, LEFT_UP);
}
else
{
ret += dfs(x – 1, y – 1, r, LEFT_DOWN);
ret += dfs(x – 1, y – 1, r, LEFT_UP);
}
return dp[x][y][r][from] = ret;
}

int main(int argc, char *argv[])
{
cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);

while (cin >> N >> R >> K)
{
memset(dp, –1, sizeof(dp));
cout << dfs(2 * N, 0, R, LEFT_UP) << '\\n';
}

return 0;
}

总结

本题通过记忆化搜索统计满足特定峰值条件的格路径数量。关键在于正确定义状态和转移方程,特别是峰值的判定条件:当从上升步骤转为下降步骤时,若当前高度为 kkk,则形成一个峰值。利用 n<20n < 20n<20 的小规模,动态规划可以高效解决问题。代码中使用 memset 初始化记忆化数组,并通过递归实现状态转移,逻辑清晰且易于理解。

赞(0)
未经允许不得转载:171主机测评 » UVa 986 How Many?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址