dfs
记忆化搜索、动态规划。
是中文题,但是题意没说清楚,反正把我恶心到了。你是真的牛批。
两个地方没说清楚。
- 实际上每到一条路径终点之后就清除身上携带的能量值(若没用完),然后再加上这个路径终点的能量值。
若按我理解的那样,则每个点的状态要用三维数组表示,其中第三维表示在当前点含有e
能量值到终点的路数。可知当e>=N-x+M-y
时的状态都等价。 - 这个题中说的总体路线是一条条路径组成的,它不关心每条路径的具体走法,它只关心每条路径的路径终点是什么。也就是说,总体路线是这样标识的:(
(1,1)
,路径终点,路径终点,。。。,路径终点,(N,M)
)。这个序列不同则路线不同,这个序列相同则路线相同。
若按我理解的那样,每条路径还要计算有多少种走法,然后dp[x][y] += num[dx][dy]*dp[x+dx][y+dy]
。(计算这个不难,dp转移式:num[dx][dy] = num[dx-1][dy]+num[dx][dy-1]
,dx
,dy
表示两点间的坐标差值)
然后再说几个注意的地方:
- 如果把
dp
的初始值设为0
,则与计算出来dp
真的等于0
的那些值混淆了,这就意味着每次这些“真”0
值都无法被记忆化了。所以我们设初始值都为-1
,彻底区分未计算值和已计算值。(但这样的话,每次计算前别忘了先清零) - 直接枚举增量,然后在第二层循环
for
内部直接判断dx+dy<=a[x][y]
,这样枚举的三角形斜边就出来了,很巧妙。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int MOD = 10000;
const int MAX = 101;
int N, M, T;
int a[MAX][MAX];
//int num[MAX][MAX]; // 没用了,人家不关心你是怎么走到下一跳的
int dp[MAX][MAX];
void init()
{
//memset(num, 0, sizeof num);
memset(dp, -1, sizeof dp);
}
//void run1()
//{
// for (int i = 0; i < MAX; i++)
// num[0][i] = num[i][0] = 1;
// for (int i = 1; i <= N - 1; i++)
// for (int j = 1; j <= M - 1; j++)
// num[i][j] = num[i - 1][j] + num[i][j - 1];
//}
int run(int x, int y)
{
if (x == N && y == M) return 1;
if (dp[x][y] != -1) return dp[x][y];
dp[x][y] = 0; // 因为把初始值都弄成-1了,所以这里还要再清零
for (int dx = 0; dx <= a[x][y]; dx++)
{
if (x + dx > N) break; // 之后的dx只会更大,所以可以退出循环了
for (int dy = 0; dx + dy <= a[x][y]; dy++) // 这里的限制条件提前到循环中判断,很巧妙
{
if (y + dy > M) break;
if (dx == 0 && dy == 0) continue;
dp[x][y] += run(x + dx, y + dy); // 这里不用模
}
}
return dp[x][y] %= MOD; // 这里的“=”别丢了,不然以后再引用的是原值
}
int main()
{
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d", &N, &M);
init();
for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++)
scanf("%d", &a[i][j]);
//run1();
printf("%d\n", run(1, 1));
}
return 0;
}