题目链接:

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;
}
posted on 2021-11-11 15:17  Hamine  阅读(70)  评论(4编辑  收藏  举报