【HDU 1005 && ZOJ 3539】简单矩阵dp

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1005

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3538

1005题目大意:

告诉你f[1]=1, f[2]=1, f[n]=(A*f[n-1]+B*f[n-2])%7;然后输入A,B,n,让你求f[n]。

 

解题思路:

解法1:n比较大,给你这样递推式子一般不可能让你全部求出来,一般是有规律可寻的。只要在递推的过程中发现f[n-1]==f[1],f[n]==f[2],停止递推。把它多少个数循环一次记录下来,然后只需要用n对这个数取余即可。

解法2: 巧用矩阵dp 。 一般的矩阵dp是要你自己推出这个递推式,然后再构造矩阵。这题更简单一些,因为题目已经给好了你递推式,f[1],f[2]为特殊项,这里我们不考虑,把f[3]当做第一项来考虑。

f[3]=A+B,   f[4]=A*(A+B)+B

这里递推式可以分解为两项,可以先把[A B](f[3]的两项)提出来,一般的矩阵dp为2阶,所以这样还是不够的。再观察递推式,求f[n+1]时我们还要用到前面两项,f[n]就在之前,所以我们还要把前面出现的f[n-1]保存下来,即让A对应的为1。这样就可以构造矩阵 | A B | 了。

                               | 1 0 |

构造完矩阵其他的就简单了,简单的矩阵快速幂。

解法一AC代码

View Code

解法二AC代码

View Code
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 #define mod 7
 8 int  a, b, n;
 9 
10 struct Maxtri
11 {
12      int mat[2][2];
13 };
14 
15 Maxtri A, B;
16 
17 void init()
18 {
19     A.mat[0][0]=1,A.mat[0][1]=0;
20     A.mat[1][0]=0,A.mat[1][1]=1;
21     B.mat[0][0]=a,B.mat[0][1]=b;
22     B.mat[1][0]=1,B.mat[1][1]=0;
23 }
24 
25 
26 Maxtri Maxtri_mul(Maxtri a, Maxtri b)
27 {
28     Maxtri c;
29     for(int i=0; i<2; i++)
30         for(int j=0; j<2; j++)
31         {
32             c.mat[i][j]=0;
33             for(int k=0; k<2; k++)
34             c.mat[i][j]+=(a.mat[i][k]*b.mat[k][j])%mod;
35             c.mat[i][j]%=mod;
36         }
37 
38     return c;
39 }
40 
41 int Maxtri_mi(int b)
42 {
43     Maxtri ans=A, tp=B;
44     while(b)
45     {
46         if(b&1)
47              ans=Maxtri_mul(ans,tp);
48         b>>=1;
49         tp=Maxtri_mul(tp,tp);
50     }
51     return (ans.mat[0][0]+ans.mat[0][1])%mod;
52 }
53 
54 int main()
55 {
56     while(~scanf("%d%d%d",&a,&b,&n))
57     {
58         init();
59         if(a+b+n==0) break;
60         if(n<3)
61         {
62             cout << 1 <<endl; continue;
63         }
64         int ans=Maxtri_mi(n-2);
65         printf("%d\n",ans);
66     }
67     return 0;
68 }

3538:

解题思路: A____(a种填法)____B____(b种填法)____C____(c种填法)____C____(d种填法)____D

题目的答案可以转换成a*b*c*d。

这里我令dp[i][0]表示两边字母相同中间有i个位置有多少种填法,dp[i][1]表示两边字母不同中间有i个位置多少种填法。

dp[1][0]=3,dp[1][1]=2,dp[2][0]=6,dp[2][1]=7。

可以找到递推式 dp[i][0]=3*dp[i-1][1];  dp[i][1]=dp[i-1][0]+2*dp[i-1][1];

这里找的构造矩阵是|0 1|

                           |3 2|

还要注意的两点:

1、要特判第一个字符前面和最后一个字符后面的空位置。

2、中间要特判两个字符是否相邻,相邻不符合题目,直接输出0.

View Code
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 using namespace std;
  6 
  7 #define mod 1000000007
  8 typedef long long lld;
  9 
 10 struct Node
 11 {
 12     int d;
 13     char str[5];
 14     bool operator<(const Node &S)const
 15     {
 16         return d<S.d;
 17     }
 18 }f[20];
 19 
 20 struct Maxtri
 21 {
 22     lld mat[2][2];
 23 };
 24 
 25 Maxtri A, B;
 26 
 27 void init()
 28 {
 29     A.mat[0][0]=1,A.mat[0][1]=0;
 30     A.mat[1][0]=0,A.mat[1][1]=1;
 31     B.mat[0][0]=0,B.mat[0][1]=1;
 32     B.mat[1][0]=3,B.mat[1][1]=2;
 33 }
 34 
 35 lld Maxtri_mod(lld a, int b)
 36 {
 37     lld ans=1;
 38     while(b)
 39     {
 40         if(b&1)
 41             ans=(ans*a)%mod;
 42         b>>=1;
 43         a=(a*a)%mod;
 44     }
 45     return ans;
 46 }
 47 
 48 Maxtri Maxtri_mul(Maxtri a, Maxtri b)
 49 {
 50     Maxtri c;
 51     for(int i=0; i<2; i++)
 52         for(int j=0; j<2; j++)
 53         {
 54             c.mat[i][j]=0;
 55             for(int k=0; k<2; k++)
 56             c.mat[i][j]+=(a.mat[i][k]*b.mat[k][j])%mod;
 57             c.mat[i][j]%=mod;
 58         }
 59 
 60     return c;
 61 }
 62 
 63 lld Maxtri_mi(int b, int p)
 64 {
 65     Maxtri ans=A, tp=B;
 66     while(b)
 67     {
 68         if(b&1)
 69              ans=Maxtri_mul(ans,tp);
 70         b>>=1;
 71         tp=Maxtri_mul(tp,tp);
 72     }
 73     if(p==0)
 74       return ans.mat[1][0]%mod;
 75     else
 76       return  ans.mat[1][1]%mod;
 77 }
 78 
 79 int main()
 80 {
 81     int n, m;
 82     init();
 83     while(~scanf("%d%d",&n,&m))
 84     {
 85         if(m==0)
 86         {
 87             lld ans=Maxtri_mod(3,n-1)*4%mod;
 88             printf("%lld\n",ans);
 89             continue;
 90         }
 91         for(int i=0; i<m; i++)
 92             scanf("%d %s",&f[i].d,f[i].str);
 93         sort(f,f+m);
 94         lld ans=Maxtri_mod(3,f[0].d-1)%mod;
 95         for(int i=1; i<m; i++)
 96         {
 97             if(f[i].d-f[i-1].d-1==0) ///!!!
 98             {
 99                 if(*f[i].str==*f[i-1].str) 
100                 {
101                      cout << 0 <<endl; goto loop;  ///直接跳出循环到loop
102                 }
103                 else continue;
104             }
105             else
106             {
107                 if(*f[i].str==*f[i-1].str) /// 开始忘记打*号比较值,不打则是比较地址
108                    ans=ans*Maxtri_mi(f[i].d-f[i-1].d-1,0)%mod;
109                 else
110                    ans=ans*Maxtri_mi(f[i].d-f[i-1].d-1,1)%mod;
111             }
112         }
113         ans=ans*Maxtri_mod(3,n-f[m-1].d)%mod;
114         printf("%lld\n",ans);
115         loop:{}
116     }
117     return 0;
118 }
原文地址:https://www.cnblogs.com/kane0526/p/2811184.html