[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][k] = \sum_{j=0}^{m-1}f[i-1][j]*g[j][k]$

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

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

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

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

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

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

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

然后我们考虑优化:

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

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

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

 

------update in   21/5/12 ------   今天翻到这道题感觉有些地方没说清楚,等我考完期中来完善一下

 

  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\n", 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\n", 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 }

 

 

posted @ 2018-07-19 00:45  ww3113306  阅读(174)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。