stick_矩阵乘法
来源:互联网 发布:class属性php 编辑:程序博客网 时间:2024/06/06 00:12
好吧, 重新写一写矩阵乘法。
考虑斐波拉契数列 Fi = Fi-1+ Fi-2
那么考虑一个1*2的矩阵{ Fi, Fi-1 } 乘以一个 2*2的矩阵, 成为{ Fi+1 , Fi}
考虑矩阵乘法的定义, 这个一维数组的第一个数即为 原一位数组的每一个数 乘以 2*2矩阵的第一列数;
第二个数即 原一位数组的每一个数 乘以 2*2矩阵的第二列数;
以此类推。
所以因为Fi+1 = Fi + Fi-1 所以 第一列数为 {1, 1}
Fi= Fi 所以第二列数为{ 0, 1}
我们可以通过递推式来确定矩阵。
再考虑另一个操作。
A= A+B-C
B= 2A+C
C= B-A
把这三个数进行n次操作。
考虑一维矩阵(A, B, C)- 乘以一个矩阵, 使之成为新的A,B,C
这个矩阵为
{ 1, 2, -1,
1, 0, 1
-1, 1 , 0}
这是根据系数得到的。
再看看一道题。Fibonacci的变种。
Fi= Fi-1 + Fi-2 + i
首先考虑状态有几个数~
两个数是不行的~~~~那我们设状态为 {Fi , Fi-1, i} 能不能推出 {Fi+1, Fi, i+1}
这里包括了两个操作: Fi= Fi-1 + Fi-2 + i; i+1= i + 1;那我们发现后一个是求不出来的~~因为不能实现自增操作~~那么只能在加多一个数“1”。
所以状态变成了{Fi, Fi-1, i, 1}
于是, 矩阵就是:
{
1, 1, 0, 0
1, 0, 0, 0
1, 0, 1, 0
0, 0, 1, 1
}
言归正传, 看看题目。 时限5s。<最好是1s过>
【问题描述】
CD拿出来了好多1×1×2的积木,准备按像这样的俯视图的方式堆积木:
XXX
XOX
XXX {O是空的}
正如你看到的,是一个3×3中间是空的正方形!
例如,对于第一层,其中一种方式可以是这样的:
AAB
COB
CDD
现在,CD计划堆一个高度为N层的积木(除了中心外,其他地方不能留空),那么,他又多少种堆积木的方法呢?
【输入】
输入一行,包含一个整数N(1<=1000000000)。
【输出】
输出所有情况的数量对1000000007取模后的结果。
这道题递推是比较明显的递推, 设F[i, j]为到了第i层, i层以下以被填满,第i层 还没有被填满, 但已经被i-1层竖起来的积木占用了状态为j的位置。
由于只有8个位置,总状态数为2^8=256个。
递推式可以枚举每一种状态, 再用深搜摆满, 记录每一种合法的状态的竖着摆放的状态。即 x 可以 推向 y。
则 F[i+1, k] += F[ i, j ] (状态j可以推向状态k)
由于i维巨大, 考虑矩阵乘法。
将F[i] 【一个一维矩阵】 乘以一个矩阵【2维】 得到 F[i+1]
矩阵可以由递推式得到。 自行YY。
由于矩阵乘法有结合律。所以可以先计算这个矩阵 G 【即上面求出的2维矩阵】 的 n次方 算出来, 再与 一维矩阵相乘。
于是可以使用快速幂。
x^y = (x^ y/2)^2 (y mod 2=0)
x^y = (x^ y/2)^2 * x (y mod 2=1)【此处为整除】
矩阵乘法的时间复杂度为N^3
于是时间为 O(256^3 * log N )
The End.
超级优化:
把无用的状态去掉。【把从0开始搜索到的状态才记录下来】
于是时间可以优化到(70^3 * log N)
再贴一下恶心的代码~~~
#include<cstdio>#include<cstring>using namespace std;#define N 256#define Mod 1000000007const int pair[8][2]={1,3, 0, 2, 1, 4, 0, 5, 2, 7, 3, 6, 5, 7, 4, 6};typedef long long int64;typedef int64 mat[N][N];mat yuan, now, tp;int n, O;int lab[N], que[N];bool f[8], g[8], w[N];void cha(int x){ memset(f, 0, sizeof(f)); for(int i=0; i<=7; i++, x>>=1) f[i]= (x & 1); } int coding(bool f[]){ int ret=0; for(int i=7; i>=0; i--) {ret=ret*2; if(f[i]) ret++;} return ret;}void dfs(int x, int y){ if(y==8){ int code= coding(g); yuan[lab[code]][x]++; //yuan[x][code]++; return; } if(f[y]){ dfs(x, y+1); return; } int p= pair[y][0], q=pair[y][1], ty=y+1; if(!f[p]){ f[y]=f[p]=1; dfs(x, y+1); f[y]=f[p]=0;} if(!f[q]){ f[y]=f[q]=1; dfs(x, y+1); f[y]=f[q]=0;} f[y]=g[y]=1; dfs(x, y+1); f[y]=g[y]=0; return; }void expand(int y){ // 我竟然写了和一个dfs一样的过程来求有用的状态!!! if(y==8){ int code= coding(g); if(lab[code]<0) {que[++O]=code; lab[code]=O;} return; } if(f[y]){ expand(y+1); return; } int p= pair[y][0], q=pair[y][1], ty=y+1; if(!f[p]){ f[y]=f[p]=1; expand(y+1); f[y]=f[p]=0;} if(!f[q]){ f[y]=f[q]=1; expand(y+1); f[y]=f[q]=0;} f[y]=g[y]=1; expand(y+1); f[y]=g[y]=0; return; }void graph(){ memset(lab, 255, sizeof(lab)); O=0; que[0]=0; lab[0]=0; int t=0; while(t<=O){ cha(que[t]); memset(g, 0 ,sizeof(g)); expand(0); ++t; } for(int i=0; i<N; i++)if(lab[i]>=0){ cha(i); memset(g, 0 ,sizeof(g)); dfs(lab[i], 0); } } void mult(mat a, mat b){ memset(tp, 0, sizeof(tp)); for(int i=0;i<=O;i++) for(int j=0;j<=O;j++) for(int k=0;k<=O;k++) tp[i][j]= (tp[i][j] + b[i][k] * a[k][j] )% Mod; memcpy(now, tp, sizeof(tp));} void solve(){ int tn=n, tp=0; while(tn>0){ w[++tp]= (tn & 1); tn>>=1; } memcpy(now, yuan, sizeof(now)); for(int i=tp-1; i>0; i--){ mult(now, now); if(w[i]) mult(yuan ,now); } printf("%d\n", now[0][0]); } int main(){ freopen("stick.in","r",stdin); freopen("stick.out","w",stdout); scanf("%d",&n); graph(); solve(); fclose(stdin);fclose(stdout); return 0;}
- stick_矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 矩阵乘法
- 输出该字符串中字符的所有组合
- 关于SQL中查找字符串的总结 PATINDEX CHARINDEX 返回字符串中第N次出现指定字符串位置
- AVR熔丝位的设置分析
- Tour-----最佳二分匹配
- hdu 4373 Mysterious For 【Lucas定理】
- stick_矩阵乘法
- GwtUpload_GettingStarted
- 并查集
- 有序的循环链表中插入结点
- 还在愁csdn博客上不去吗
- Creating PDF with Java and iText - Tutorial
- HD 1091
- .NET生成静态页面例子
- Quick Starter on Parser Grammars