题目链接:
POJ : http://39.98.219.132:8080/problem/1379
计蒜客 : https://nanti.jisuanke.com/t/31434
题目大意:
在 w * h 的网格中,(1,1)在左下角,(w,h)在右上角,只能向右上角移动,且每次移动的起点和终点的坐标差不大于 k,即你只能从当前坐标为左下角的边长为 k + 1 的正方形中选择一个方格作为目的地,但不能不移动,计算从(1,1)出发有多少种方案能到(w,h)。
思路:
跑 暴力,设 \(sum[i][j]\) 为以 \((i,j)\) 为右上角,(1,1) 为左下角的正方形方案数的总和,\(dp[i][j]\) 为到 \((i,j)\) 这个位置的方案数,\(dp[i][j]\) = \(\sum_{x = i - k}^{i}\)\(\sum_{y = j - k}^{j}\)( (x == i && y == j) ? 0 : \(dp[x][y]\) ),时间复杂度为 O(w * h * k * k),拿到 60分 。
考虑 二维前缀和 ,
dp[i][j]
= (sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1]) - (sum[i - k - 1][j] + sum[i][j - k - 1] - sum[i - k - 1][j - k - 1])。
那么可以得到
sum[i][j]
= sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + dp[i][j]
= 2 * (sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1]) - (sum[i - k - 1][j] + sum[i][j - k - 1] - sum[i - k - 1][j - k - 1])
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int MAX = 5010;
const int MOD = 998244353;
LL w, h, k, sum[MAX][MAX], x, y, ans;
int main(){
cin >> w >> h >> k;
sum[1][1] = 1;
for (LL i = 1; i <= w; i++){
for (LL j = 1; j <= h; j++){
if (i == 1 && j == 1) continue;
x = max(i - k - 1, 0 * 1LL), y = max(j - k - 1, 0 * 1LL);
sum[i][j] = (2 * (sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]) % MOD - (sum[x][j] + sum[i][y] - sum[x][y]) % MOD) % MOD;
while (sum[i][j] < 0) sum[i][j] += MOD;
}
}
ans = sum[w][h] - sum[w - 1][h] - sum[w][h - 1] + sum[w - 1][h - 1];
while (ans < 0) ans = (ans + MOD) % MOD;
cout << ans << "\n";
return 0;
}