HDU 4832 Chess
2015-09-06 18:27
387 查看
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=4832题目大意
n×mn \times m的网格里面,(x0,y0)(x_0, y_0)位置有个棋子,每步棋子可以向上下左右走 1 或 2 格。现在这个棋子要走k步,问有几种走法?两种走法不同当仅当存在某一步不同。
数据范围
1≤n≤10001 \le n \le 10001≤m≤10001 \le m \le 1000
1≤k≤10001 \le k \le 1000
1≤x0≤n1 \le x_0 \le n
1≤y0≤m1 \le y_0 \le m
思路
基本
1
如果是无限大,result=8kresult = 8^k。2
dp[i][j][k] 表示第 k 步走到 (i, j) 的方法数目:dp[i][j][k]result=∑t=18dp[it][jt][k−1]=∑i,jdp[i][j][k]
\begin{align}
dp[i][j][k] &= \sum\limits_{t = 1}^8 dp[i_t][j_t][k - 1]\\
result &= \sum\limits_{i, j}dp[i][j][k]
\end{align}
当然时间是不够的。
突破口
要求 k 步走到 (p, q) 的方法数目,可以假设横方向走了 t 步,纵方向走了 k-t 步。这样的条件下,走法数目可以这样表示:Ctkf(t,p)g(k−t,q)
C_k^t f(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)
result = \sum\limits_{p, q}\sum_{t = 0}^k C_k^t f(t, p) g(k - t, q)
c,f,g 都可以预处理出来,时间是 O(nm+k(n+m))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)
\begin{align}result &= \sum\limits_{p, q}\sum_{t = 0}^k C_k^t f(t, p) g(k - t, q)\\
&= \sum\limits_{t = 0}^k C_k^t\sum\limits_{p, q}f(t, p) g(k - t, q)\\
&= \sum\limits_{t = 0}^k C_k^t\sum\limits_{p = 1}^nf(t, p) \sum\limits_{q = 1}^mg(k - t, q)
\end{align}
这样就可以 O(k(n+m))O(k(n + m))解决这个问题。
P.S.
其实再处理 f,g 的前缀和的话可以 O(k)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; }
相关文章推荐
- 用JQuery中的方法实现跨域请求
- 关于Oracle和SQLServer数据库在.net中拼接数据库语句的不同
- ios 避免navigationcontroller出现时scrollview内容被resize
- PAT 1088. Rational Arithmetic (20)
- 在状态栏加进度条方式
- 归并排序
- php preg_match_all 和 str_replace 替换图片链接
- 黑马程序员---网络编程(TCP传输 二)
- mysql主从
- Effective STL: 使用swap来修整过剩容量
- hadoop eyes开发进度
- ios 转json字符串
- C#设置IE代理
- 更新日志 - fir.im 回归,上线 Android Studio 插件
- List Set Map
- Java IO学习总结
- apache+php安装(centos6.5)
- 动态规划
- MySQL学习笔记—基本操作汇总
- 指定时间执行任务