zoukankan      html  css  js  c++  java
  • [题解][SHOI2013]超级跳马 动态规划/递推式/矩阵快速幂优化

    这道题... 让我见识了纪中的强大

    这道题是来纪中第二天(7.2)做的,这么晚写题解是因为

    我去学矩阵乘法啦啦啦啦啦
    对矩阵乘法一窍不通的童鞋戳链接啦

    层层递推会TLE,正解矩阵快速幂

    首先题意就是给你一个 n 行m 列 的格子图 一只马从棋盘的左上角跳到右下角。每一步它向右跳奇数列,且跳到本行或相邻行。

    题意很简单
    暴力dp的思路也很简单
    但是数据很恶心
    虽然远古一点,但毕竟是省选题

    1 ≤ n ≤ 50,2 ≤ m ≤ 10^9

    不过还是给了我们一点提示:
    n这么小?

    总之我们先找出转移式
    对于每一个点 (i,j) 的
    我们可以从它左边所有奇数行跳过来
    所以DP[i][i]=sum( 左边间隔偶数列上中下三行的和 )
    如果每个点都往前找一次的话
    这样复杂度是O(n2m)
    得分10

    所以我们想到了前缀和
    别问我怎么想到的,有什么套路
    这种东西真的是灵感

    DP[i][j]保存 (i-1)列+(i-3)列......上中下三行的和
    这么简化之后,我们的递推式就好写了
    时间复杂度O(mn)
    甚至可以用滚动数组优化
    这样空间也不是问题了O(n)
    得分50

    DP[i][j] = DP[i-2][j] + DP[i-1][j+1] + DP[i-1][j] + DP[i-1][j-1]

    那么怎么得满分呐?
    敲黑板划重点啦

    观察一下递推式......嗯?递推式啊
    那就用矩阵快速幂吧
    不过这道题的递推关系有点复杂啊___二维递推

    越到这种时候越要冷静观察,感性思考

    再吱一声:不会矩阵快速幂的同学戳链接

    观察一下,递推式需要两行数据
    我们把这两行看成两个数据
    跟斐波那契数列的递推矩阵一样放在一行

    展开来就是像这样的东西(以n=4为例)


    再展开

    虽然这么看很乱(个P),不过我们只要仔细思考其中的意义就不难明白了

     这样写下来之后,我们所需要的 DP[i-2][j] , DP[i-1][j+1] , DP[i-1][j] , DP[i-1][j-1]
    就都出现在矩阵L中了

    开始填矩阵R

    根据矩阵乘法左行右列的规则,每次乘法 矩阵L 的每个元素都有机会被乘到
    我们可以在填矩阵的时候自己选择系数(多方便啊)

    由于我们的矩阵是 2n 的 , 所以我们需要一个2n*2n的正方形矩阵(我都以4为例呐)


    (这是一个已经填好的矩阵)

    答案A 的第一行 第一列 等于 矩阵L的第一行 * 矩阵R的第一列(详见矩阵乘法详解)
    由于系数都是1,所以我们填1

    我还是随便解释一个吧
    不然跟其他的题解有什么区别

    DP[i][3] = DP[i-2][3] + DP[i-1][2] + DP[i-1][3] + DP[i-1][4]

    所以在这个矩阵中,第3列是这么乘的

    大家一定都懂了

    对吧....

    虽然n是不同的,但是矩阵的构造是相似的
    所以我写了一个函数来初始化数组L和R

     1 void _make(int A[LEN][LEN], int B[LEN][LEN], int len) {
     2     for (int i = 1; i <= len; ++i) {
     3         for (int j = 1; j <= len; ++j) {
     4             if (i == j)
     5                 A[i][j] = A[len + i][j] = A[i][len + j] = 1;
     6             if (i - j == 1 || j - i == 1)
     7                 A[i][j] = 1;
     8         }
     9     }
    10     B[1][1] = B[1][2] = B[1][len + 1] = 1;
    11 }
    _make

     然后矩阵快速幂就完事了(详细过程见淼淼的矩阵快速幂详解

    来了源码

     1 // 2
     2 
     3 #include <iostream>
     4 using namespace std;
     5 
     6 
     7 #define LEN 100
     8 #define MOD 30011
     9 
    10 unsigned int n, m;
    11 
    12 int mat[LEN][LEN] = {0}, ans[LEN][LEN];
    13 
    14 void _make(int A[LEN][LEN], int B[LEN][LEN], int len);
    15 void _mul(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int z);
    16 void _mi(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int m);
    17 
    18 int main() {
    19     freopen("2.in", "r", stdin);
    20     cin >> n >> m;
    21     if (m == 1) {
    22         cout << ((n == 1) ? (1) : (0));
    23         return 0;
    24     }
    25     _make(mat, ans, n);
    26     _mi(ans, mat, ans, 1, 2 * n, m - 3);
    27     cout << (ans[1][n - 1] + ans[1][n]) % MOD;
    28     return 0;
    29 }
    30 
    31 void _make(int A[LEN][LEN], int B[LEN][LEN], int len) {
    32     for (int i = 1; i <= len; ++i) {
    33         for (int j = 1; j <= len; ++j) {
    34             if (i == j)
    35                 A[i][j] = A[len + i][j] = A[i][len + j] = 1;
    36             if (i - j == 1 || j - i == 1)
    37                 A[i][j] = 1;
    38         }
    39     }
    40     B[1][1] = B[1][2] = B[1][len + 1] = 1;
    41 }
    42 
    43 void _mul(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int z) {
    44     long long cmp[LEN][LEN] = {0};
    45     for (int i = 1; i <= x; ++i)
    46         for (int j = 1; j <= z; j++)
    47             for (int k = 1; k <= y; k++)
    48                 cmp[i][j] = (cmp[i][j] + L[i][k] * R[k][j]) % MOD;
    49     for (int i = 1; i <= x; ++i)
    50         for (int j = 1; j <= z; ++j)
    51             A[i][j] = cmp[i][j];
    52 }
    53 
    54 void _mi(int L[LEN][LEN], int R[LEN][LEN], int A[LEN][LEN], int x, int y, int m) {
    55     int cmp[LEN][LEN];
    56     for (int i = 1; i <= x; ++i)
    57         for (int j = 1; j <= y; ++j)
    58             cmp[i][j] = L[i][j];
    59     while (m > 0) {
    60         if (m & 1)
    61             _mul(cmp, R, cmp, x, y, y);
    62         m >>= 1;
    63         _mul(R, R, R, y, y, y);
    64     }
    65     for (int i = 1; i <= x; ++i)
    66         for (int j = 1; j <= y; ++j)
    67             A[i][j] = cmp[i][j];
    68 }
    超级跳马
  • 相关阅读:
    MyBatis环境配置
    log4j配置不同的类多个日志文件
    Http协议头、代理
    Apache二级域名实现
    Flash Builder 4.7 完美破解
    网页设计方面,哪些中英文字体的组合能有好的视觉效果
    网页设计中最常用的字体
    sublime text 3 插件:package control
    大量实用工具类、开源包,该帖绝对值得你收藏!
    10个简化Web开发者工作的HTML5开发工具
  • 原文地址:https://www.cnblogs.com/mxxr/p/11172855.html
Copyright © 2011-2022 走看看