HDU 4832 Chess(DP+组合数学)(2014年百度之星程序设计大赛 - 初赛(第二轮))

Problem Description
  小度和小良最近又迷上了下棋。棋盘一共有N行M列,我们可以把左上角的格子定为(1,1),右下角的格子定为(N,M)。在他们的规则中,“王”在棋盘上的走法遵循十字路线。也就是说,如果“王”当前在(x,y)点,小度在下一步可以移动到(x+1, y), (x-1, y), (x, y+1), (x, y-1), (x+2, y), (x-2, y), (x, y+2), (x, y-2) 这八个点中的任意一个。


  
图1 黄色部分为棋子所控制的范围

  小度觉得每次都是小良赢,没意思。为了难倒小良,他想出了这样一个问题:如果一开始“王”在(x0,y0)点,小良对“王”连续移动恰好K步,一共可以有多少种不同的移动方案?两种方案相同,当且仅当它们的K次移动全部都是一样的。也就是说,先向左再向右移动,和先向右再向左移动被认为是不同的方案。
  小良被难倒了。你能写程序解决这个问题吗?
 
Input
输入包括多组数据。输入数据的第一行是一个整数T(T≤10),表示测试数据的组数。
每组测试数据只包括一行,为五个整数N,M,K,x0,y0。(1≤N,M,K≤1000,1≤x0≤N,1≤y0≤M)
 
Output
对于第k组数据,第一行输出Case #k:,第二行输出所求的方案数。由于答案可能非常大,你只需要输出结果对9999991取模之后的值即可。
 
题目大意:略。
思路:很容易想到一个朴素的DP,dp[i][j][k]代表走k步走到坐标(i, j)的方案数,复杂度为O(nmk),超时的节奏。
仔细想想,可以发现竖着走和横着走是独立的,于是分开考虑。
k步中,选i步横着走,那么有k-i步是竖着走的,设sumx[i]为横着走i步的方案数,sumy[k-i]为竖着走k-i步的方案数。
那么 ans = sum{c[k][i] * sumx[i] * sumy[k-i], 0 ≤ i ≤ k},其中c[k][i]为组合数,意味在k步中选择i步横着走。
其中sumx[]可以用dp[i][j]表示走k步走到坐标i(一维)的方案数,复杂度为O(nk),sum[y]同理。
于是总复杂度降到O(nk + mk)。
 
代码(750MS);
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 #include <vector>
 6 using namespace std;
 7 typedef long long LL;
 8 
 9 const int MOD = 9999991;
10 const int MAXN = 1010;
11 
12 int f[] = {-2, -1, 1, 2};
13 
14 int n, m, k, x0, y0, T;
15 int dpx[MAXN][MAXN], dpy[MAXN][MAXN];
16 int sumx[MAXN], sumy[MAXN];
17 int c[MAXN][MAXN];
18 
19 void initc() {
20     int n = 1000;
21     c[0][0] = 1;
22     for(int i = 1; i <= n; ++i) {
23         c[i][0] = 1;
24         for(int j = 1; j <= i; ++j)
25             c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
26     }
27 }
28 
29 bool check(int x, int n) {
30     return 1 <= x && x <= n;
31 }
32 
33 int solve() {
34     memset(dpx, 0, sizeof(dpx));
35     dpx[0][x0] = 1;
36     for(int p = 1; p <= k; ++p) {
37         for(int i = 1; i <= n; ++i) {
38             for(int v = 0; v < 4; ++v) {
39                 int t = i + f[v];
40                 if(check(t, n)) dpx[p][t] = (dpx[p][t] + dpx[p - 1][i]) % MOD;
41             }
42         }
43     }
44 
45     memset(sumx, 0, sizeof(sumx));
46     for(int i = 0; i <= k; ++i) {
47         for(int j = 1; j <= n; ++j) sumx[i] = (sumx[i] + dpx[i][j]) % MOD;
48     }
49 
50     memset(dpy, 0, sizeof(dpy));
51     dpy[0][y0] = 1;
52     for(int p = 1; p <= k; ++p) {
53         for(int i = 1; i <= m; ++i) {
54             for(int v = 0; v < 4; ++v) {
55                 int t = i + f[v];
56                 if(check(t, m)) dpy[p][t] = (dpy[p][t] + dpy[p - 1][i]) % MOD;
57             }
58         }
59     }
60 
61     memset(sumy, 0, sizeof(sumy));
62     for(int i = 0; i <= k; ++i) {
63         for(int j = 1; j <= m; ++j) sumy[i] = (sumy[i] + dpy[i][j]) % MOD;
64     }
65 
66     LL ans = 0;
67     for(int i = 0; i <= k; ++i)
68         ans = (ans + LL(c[k][i]) * sumx[i] % MOD * sumy[k - i]) % MOD;
69 
70     return (int)ans;
71 }
72 
73 int main() {
74     initc();
75     //cout<<c[1000][1000]<<endl;
76     scanf("%d", &T);
77     for(int t = 1; t <= T; ++t) {
78         scanf("%d%d%d%d%d", &n, &m, &k, &x0, &y0);
79         printf("Case #%d:\n", t);
80         printf("%d\n", solve());
81     }
82 }
View Code

 

posted @ 2014-05-25 20:21  Oyking  阅读(296)  评论(0编辑  收藏  举报