• ZOJ 3256 Tour in the Castle 插头DP 矩阵乘法


    题解  

      这题是一道非常好的插头题,与一般的按格转移的题目不同,由于m很大,要矩阵乘法,这题需要你做一个按列转移的插头DP。

      按列转移多少与按格转移不同,但大体上还是基于连通性进行转移。每一列只有右插头是对下一列的转移有影响的,那么我们只需要记录每一列的右插头的连通情况,用最小表示法表示为当前列的状态。在转移的时候,我们只知道上一列的右插头,即本列的左插头的情况,而上插头还需要自己记一个标记。

      那么我们具体分析:

      1、不存在上插头

        1、同时存在左插头和右插头,不需要修改当前插头,直接把上一列的右插头当做当前列的右插头

        2、只在左插头,即从上一列的某一个连通块转移过来,记录连通块。(左下插头)

        3、只在右插头,即此为一个新的连通块,打上标记,表明这是一个新的连通块。(右下插头)

      2、存在上插头

        1、同时存在左插头和右插头,一个格子里有三个插头,非法状态

        2、都不存在左插头和右插头,不需要修改当前插头,即从上往下。

        3、存在左插头

          1、上插头和左插头同属一个连通块,但不在最终状态(没有右插头)的右下角的格子里出现,非法状态

          2、上插头是左下插头,合并连通块,并删除这两个插头(这个合并比较特殊,因为两个都是已知的连通块,具体画图比较清晰)

          3、上插头是右下插头,合并连通块,删掉当前插头

        4、不存在左插头

          1、上插头是左下插头,合并连通块,删除左下插头

          2、上插头是右下插头,合并为新的连通块

      具体情况还是自己动手画图比较清晰。

      然后就到了矩乘的部分。首先考虑构造矩阵,g[i][j] = 1表示i状态能推到j状态,因此我们只需要枚举这些状态,一个一个判转移就可以了。

      初始的情况下,只存在全空的状态和0和N-1有插头的情况,因此答案就是矩阵快速幂后的ans[1][0],即从初始状态推向终止状态。

    程序

      1 #include <cstdio>
      2 #include <cstdlib>
      3 #include <cstring>
      4 #include <string>
      5 #include <algorithm>
      6 
      7 using namespace std;
      8 
      9 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
     10 #define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
     11 #define mset(a, b) memset(a, b, sizeof(a))
     12 typedef long long LL;
     13 const int MAXD = 15, HASH = 419, STATE = 1010, MOD = 7777777;
     14 int n, m, code[MAXD], ch[MAXD];
     15 struct HASHMAP
     16 {
     17     int head[HASH], nxt[STATE], state[STATE], siz;
     18     void clear() { siz = 0, mset(head, -1); }
     19     int push(int x)
     20     {
     21         int pos = x%HASH, i = head[pos];
     22         for (; i != -1; i = nxt[i])
     23             if (state[i] == x) return i;
     24         state[siz] = x;
     25         nxt[siz] = head[pos], head[pos] = siz++;
     26         return siz-1;
     27     }
     28 }hm;
     29 struct Matrix
     30 {
     31     int mat[200][200], D;
     32     Matrix operator * (const Matrix &AI) const
     33     {
     34         Matrix ret; ret.D = D;
     35         REP(i, 0, D)
     36             REP(j, 0, D)
     37             {
     38                 LL sum = 0;
     39                 REP(k, 0, D) sum += (LL)mat[i][k]*AI.mat[k][j];
     40                 ret.mat[i][j] = sum%MOD;
     41             }
     42         return ret;
     43     }
     44 }rc[30], A, B;
     45 
     46 void decode(int x)
     47 {
     48     DWN(i, n, 1) code[i] = x&3, x >>= 2;
     49 }
     50 
     51 int encode()
     52 {
     53     int cnt = 0, ret = 0;
     54     mset(ch, -1), ch[0] = 0;
     55     REP(i, 1, n)
     56     {
     57         if (ch[code[i]] == -1) ch[code[i]] = ++cnt;
     58         ret <<= 2, ret |= ch[code[i]];
     59     }
     60     return ret;
     61 }
     62 
     63 bool check(int st, int nxt)
     64 {
     65     decode(st);
     66     int up = 0, k, cnt = 0;
     67     REP(i, 1, n)
     68     {
     69         if (up == 0)
     70         {
     71             if (!code[i] && !(nxt&(1<<(i-1)))) return false;
     72             if (code[i] && (nxt&(1<<(i-1)))) continue ;
     73             if (code[i]) up = code[i];
     74             else up = -1;
     75             k = i;
     76         }
     77         else
     78         {
     79             if (code[i] && (nxt&(1<<(i-1)))) return false;
     80             if (!code[i] && !(nxt&(1<<(i-1)))) continue ;
     81             if (code[i])
     82             {
     83                 if (up == code[i] && !(nxt&(1<<(i-1))) && (nxt || i != n)) return false;
     84                 if (up != -1)
     85                 {
     86                     REP(j, 1, n) if (code[j] == code[i] && j != i) code[j] = code[k];
     87                     code[i] = code[k] = 0;
     88                 }
     89                 else code[k] = code[i], code[i] = 0;
     90             }
     91             else
     92             {
     93                 if (up != -1) code[i] = code[k], code[k] = 0;
     94                 else code[i] = code[k] = n+(++cnt);
     95             }
     96             up = 0;
     97         }
     98     }
     99     if (up) return false;
    100     return true;
    101 }
    102 
    103 void init()
    104 {
    105     if (rc[n].D != 0)
    106     { B = rc[n]; return ; }
    107     mset(rc[n].mat, 0);
    108     hm.clear(), hm.push(0); 
    109     mset(code, 0), code[1] = code[n] = 1, hm.push(encode());
    110     decode(hm.state[1]);
    111     for (int i = 1; i < hm.siz; ++i)
    112         REP(nxt, 0, ((1<<n)-1))
    113             if (check(hm.state[i], nxt))
    114             {
    115                 int j = hm.push(encode());
    116                 rc[n].mat[i][j] ++;
    117             }
    118     rc[n].D = hm.siz-1;
    119     B = rc[n];
    120 }
    121 
    122 void work()
    123 {
    124     mset(A.mat, 0); A.D = B.D;
    125     REP(i, 0, A.D) A.mat[i][i] = 1; 
    126     while (m > 0)
    127     {
    128         if (m&1) A = A*B;
    129         B = B*B, m >>= 1;
    130     }
    131     if (!A.mat[1][0]) puts("Impossible");
    132     else printf("%d
    ", A.mat[1][0]);
    133 }
    134 
    135 int main()
    136 {
    137     REP(i, 0, 20) rc[i].D = 0;
    138     while (~scanf("%d %d", &n, &m))
    139         init(), work();
    140     return 0;
    141 }
    View Code
  • 相关阅读:
    Captura
    食用Win系统自带的PowerShell登录服务器
    uTools
    图片镜像缓存服务
    博客园自定义网站ico
    超星图床
    教程翻译-理解基于矢量场寻路算法
    CentOS防火墙命令
    CentOS7的vsftpd安装和配置
    Unity网路编程-TCP实现细节备忘
  • 原文地址:https://www.cnblogs.com/-ZZB-/p/6441355.html
Copyright © 2020-2023  润新知