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) 这八个点中的任意一个。

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

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

  小度觉得每次都是小良赢,没意思。为了难倒小良,他想出了这样一个问题:如果一开始“王”在(x 0,y 0)点,小良对“王”连续移动恰好K步,一共可以有多少种不同的移动方案?两种方案相同,当且仅当它们的K次移动全部都是一样的。也就是说,先向左再向右移动,和先向右再向左移动被认为是不同的方案。
  小良被难倒了。你能写程序解决这个问题吗?
 
Input
输入包括多组数据。输入数据的第一行是一个整数T(T≤10),表示测试数据的组数。
每组测试数据只包括一行,为五个整数N,M,K,x 0,y 0。(1≤N,M,K≤1000,1≤x 0≤N,1≤y 0≤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

 

你可能感兴趣的:(程序设计)