• [HNOI2008]GT考试 矩阵优化DP


    ~~~题面~~~

    题解:

    一开始看觉得很难,理解了之后其实还挺容易的。

    首先我们考虑朴素DP:

      令f[i][j]表示长串到了第i项, 与不吉利数字(模式串)匹配到了第j项的方案。

      显然ans = f[n][0] + f[n][1] + …… + f[n][m-1];

      可以肉眼看出f[1][0] = 9, f[1][1] = 1;

      于是我们考虑如何转移。

      首先我们观察到,f[i-1][j]如果要给f[i][k]做贡献,那么就要使得匹配到j位变成匹配到k位。

      我们设g[j][k]表示原本是匹配到j位,加入一个新数字后变成匹配到k位的方案数。这里的匹配到x位的x是指匹配到模式串的第x位。

      那么我们有转移方程:$f[i][j] = sum_{i=0}^{m-1}f[i-1][k]*g[k][j]$

      那么如何求解g[i][j]呢?

      可以考虑KMP。但是由于数据比较小,所以这里就直接用暴力了。

      首先我们枚举i表示当前已经匹配到了第i位(i可以为0)

      然后我们枚举新加进来的数

      再我们再枚举可能会匹配 l 位,从高位开始枚举,

      然后暴力检验看是不是可以刚好匹配到 l 位,注意要从后面开始匹配,如果没解释清楚,看代码就知道了。

      如果可以刚好匹配到 l 位,那么我们就++g[i][l]并break

    然后我们考虑优化:

      观察转移方程$f[i][k] = sum_{i=0}^{m-1}f[i-1][j]*g[j][k]$.

      emmmm,..与矩阵相乘完美吻合。。。。

      所以用矩阵加速一下就好啦

     

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 23
      5 int n, m, p, ans;
      6 char s[AC], tmp[AC];
      7 struct matrix{
      8     int s[AC][AC];
      9 }f, g, box;
     10 
     11 void pre()
     12 {
     13     scanf("%d%d%d", &n, &m, &p);
     14     scanf("%s", s + 1);
     15     if(n == 1)
     16     {
     17         if(m == 1) ans = 9;
     18         else ans = 10;
     19         ans %= p;
     20         printf("%d
    ", ans);
     21         exit(0);
     22     } 
     23     //g.s[0][1] = 1, g.s[0][0] = 9;//因为g是匹配数,所以行也可能是0
     24     for(R i = 0; i < m ; i++)//枚举已经匹配到了哪一位
     25     {
     26         tmp[i] = s[i];
     27         for(R j = 0; j <= 9; j++)//枚举下一位是什么
     28         {
     29             tmp[i + 1] = j + '0';    
     30             int t, k = 0;
     31             for(R l = i + 1; l >= 0; l--)//枚举最多可以匹配几位
     32             {
     33                 t = l, k = i + 1;
     34                 while(s[t] == tmp[k] && t) --t, --k;
     35                 if(!t) 
     36                 {
     37                     if(l != m) ++g.s[i][l];
     38                     break;
     39                 }
     40                 
     41             }
     42         } 
     43     } 
     44 }
     45 
     46 void build()
     47 {
     48     f.s[1][1] = 1, f.s[1][0] = 9;
     49 }
     50 
     51 void cal1()
     52 {
     53     for(R j = 0; j < m; j++)//枚举是第一行的第几列,注意0也是合法的
     54     {
     55         box.s[1][j] = 0;
     56         for(R l = 0; l < m; l++)//枚举f的对应行和g的对应列
     57             box.s[1][j] += f.s[1][l] * g.s[l][j];
     58         box.s[1][j] %= p; 
     59     }
     60     f = box;
     61 }
     62 
     63 void cal2()
     64 {
     65     for(R i = 0; i < m; i++)
     66         for(R j = 0; j < m; j++)
     67         {
     68             box.s[i][j] = 0;
     69             for(R l = 0; l < m; l++)
     70                 box.s[i][j] += g.s[i][l] * g.s[l][j];
     71             box.s[i][j] %= p; 
     72         }
     73     g = box;
     74 }
     75 
     76 void qpow(int have)
     77 {
     78     while(have)
     79     {
     80         if(have & 1) cal1();
     81         cal2();
     82         have >>= 1;        
     83     }
     84 }
     85 
     86 void work()
     87 {
     88     for(R i = 0; i < m; i++) ans += f.s[1][i]; 
     89     ans %= p;
     90     printf("%d
    ", ans);
     91 }
     92 
     93 int main()
     94 {
     95     freopen("in.in", "r", stdin);
     96     pre();
     97     build();
     98     qpow(n - 1);
     99     work();
    100     fclose(stdin);
    101     return 0;
    102 }

     

     

  • 相关阅读:
    GoldenGate配置(一)之单向复制配置
    Qt4.8.6+mingw+Qgis2.4.0基于QGis的二次开发
    Linux用户及用户组设置
    HDU1013_Digital Roots【大数】【水题】
    随意一条查询sql转换为查询结果集相应的数目
    对文件地址的几种概念的理解
    2014-10深圳全球架构师峰会
    有沃更精彩,沃课堂理想的移动学习平台
    自己动手写CPU之第九阶段(8)——MIPS32中的LL、SC指令说明
    Inno Setup入门(二)——修改安装过程中的图片
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9333622.html
Copyright © 2020-2023  润新知