【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代码
解法二AC代码
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.
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 }