zoukankan      html  css  js  c++  java
  • 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:
    ", t);
    80         printf("%d
    ", solve());
    81     }
    82 }
    View Code
  • 相关阅读:
    Centos7配置局域网yum源报错——Error downloading packages: failed to retrieve packages...
    Centos7扩展根分区——不增加磁盘
    Paxos算法
    记一次业务中的大坑-MYSQL有重复数据下的增加主键
    填坑之路——Hadoop分布式缓存
    Java接口技术
    Java-IO操作性能对比
    Clob对象转换为String
    剖析Reflection.getCallerClass
    Java类的加载、链接和初始化
  • 原文地址:https://www.cnblogs.com/oyking/p/3751538.html
Copyright © 2011-2022 走看看