题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=4832
题目大意
n×m
的网格里面,
(x0,y0)
位置有个棋子,每步棋子可以向上下左右走 1 或 2 格。现在这个棋子要走k步,问有几种走法?
两种走法不同当仅当存在某一步不同。
数据范围
1≤n≤1000
1≤m≤1000
1≤k≤1000
1≤x0≤n
1≤y0≤m
思路
基本
1
如果是无限大, result=8k 。
2
dp[i][j][k] 表示第 k 步走到 (i, j) 的方法数目:
dp[i][j][k]result=∑t=18dp[it][jt][k−1]=∑i,jdp[i][j][k]
当然时间是不够的。
突破口
要求 k 步走到 (p, q) 的方法数目,可以假设横方向走了 t 步,纵方向走了 k-t 步。这样的条件下,走法数目可以这样表示:
Ctkf(t,p)g(k−t,q)
f(i, j) 表示横向走走 i 步走到横坐标是 j 的方法数;同理 g(i, j) 表示纵向。
那么:
result=∑p,q∑t=0kCtkf(t,p)g(k−t,q)
c,f,g 都可以预处理出来,时间是 O(nm+k(n+m)) ,可是直接计算上面的式子复杂度依然太高。
为此:
result=∑p,q∑t=0kCtkf(t,p)g(k−t,q)=∑t=0kCtk∑p,qf(t,p)g(k−t,q)=∑t=0kCtk∑p=1nf(t,p)∑q=1mg(k−t,q)
这样就可以 O(k(n+m)) 解决这个问题。
P.S.
其实再处理 f,g 的前缀和的话可以 O(k) 解决第二步。然而预处理时间占主要,只能优化常数级。
评价
不错哟。
代码
#include <bits/stdc++.h>
const int N = 1000 + 5;
const int M = 1000 + 5;
const int K = 1000 + 5;
const int MAXN = N + 5;
const int MAXM = M + 5;
const int MAXK = K + 5;
const int MOD = 9999991;
int c[MAXN][MAXM];
int f[MAXK][MAXN];
int g[MAXK][MAXM];
const int d[] = {1, -1, 2, -2};
void pascal_table() {
for (int i = 0; i < N; i++)
c[i][i] = c[i][0] = 1;
for (int i = 1; i < N; i++) {
for (int j = 1; j < i; j++)
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
}
}
void fg_table(int n, int m, int x, int y) {
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
f[0][x] = 1;
g[0][y] = 1;
for (int i = 1; i < K; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = 0;
for (int k = 0; k < 4; k++) {
int pre = j + d[k];
if (pre >= 1 && pre <= n)
f[i][j] += f[i - 1][pre];
}
f[i][j] %= MOD;
}
for (int j = 1; j <= m; j++) {
g[i][j] = 0;
for (int k = 0; k < 4; k++) {
int pre = j + d[k];
if (pre >= 1 && pre <= m)
g[i][j] += g[i - 1][pre];
}
g[i][j] %= MOD;
}
}
}
int main() {
pascal_table();
int casc;
scanf("%d", &casc);
for (int casi = 1; casi <= casc; casi++) {
int n, m, k;
int x0, y0;
scanf("%d %d %d", &n, &m, &k);
scanf("%d %d", &x0, &y0);
printf("Case #%d:\n", casi);
fg_table(n, m, x0, y0);
int res = 0;
for (int t = 0; t <= k; t++) {
int tmp = c[k][t];
int tmp1 = 0;
for (int i = 1; i <= n; i++)
tmp1 = (tmp1 + f[t][i]) % MOD;
int tmp2 = 0;
for (int i = 1; i <= m; i++)
tmp2 = (tmp2 + g[k - t][i]) % MOD;
tmp = (1LL * tmp * (1LL * tmp1 * tmp2 % MOD)) % MOD;
res = (res + tmp) % MOD;
}
printf("%d\n", res);
}
return 0;
}