您的位置:首页 > 其它

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 1000

1≤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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: