决心把 算法与实现的题 都做一遍,加油!
这题我这个菜鸟整整研究了一个晚上才弄明白。。。好弱啊。。
已知第i行的状态,推导出第i+1行的可行性状态,状态用0,1,表示,同一行2个0表示一个横着的骨牌,同一列1个1表示竖着的骨牌
比如 第i行为 0000,(用数字1,2,3,4表示骨牌)
那么第i+1行的状态可以为 0000 即
1122
3344
也可以为0011,即
1134
2234
也可以为1111,即
1234
1234
。
。
。
用数组t[i][j]表示i和j是否是可行性状态,比如
i行: 0100
i+1行: 1011
那么t[4][11]=1
这样计算出状态方程t
计算方法为 保证i和j二进制的每一位最多有一个1,并且i^j=0000||0011||1001||1100||1111
计算完t后,利用矩阵快速幂计算a=t^n,最后a[0][0]就是答案
至于为什么a[0][0]是答案,我感觉a[i][j]表示的以i为第一行j为最后一行的所有可能情况
当n=1是不用说了
当n=2是所有的情况都可以写成以0000为第一行的形式即
0000 0000 0000 0000 0000
0000 0011 1001 1100 1111
即1122 1134 1224 1233 1234
3344 2234 1334 1244 1234
五种情况
当n=3时
可以都可以写成
0000 0000 0000 0000 0000
0000 0011 1001 1100 1111
0000 0000 0000 0000 0000
即11种情况
所以以后无论n为奇数偶数都可由n=2和n=3的情况组合而成
而a[0][0]表示以0000开头 以0000结尾的情况 符合要求
所以a[0][0]就是答案
不知道对不对,求大牛指点
# include <stdio.h>
# include <string.h>
const int maxn=16;
int t[maxn][maxn];
int a[maxn][maxn],b[maxn][maxn];
int dz[16];
int mod;
void mul(int d[maxn][maxn],int e[maxn][maxn],int f[maxn][maxn])
{
int temp[maxn][maxn];
for(int i=0; i<maxn; i++)
{
for(int j=0; j<maxn; j++)
{
temp[i][j]=0;
for(int k=0; k<maxn; k++)
{
temp[i][j]=(temp[i][j]+d[i][k]*e[k][j])%mod;
}
}
}
for(int i=0; i<maxn; i++)
for(int j=0; j<maxn; j++)
f[i][j]=temp[i][j];
}
void pow(int n)
{
for(int i=0; i<maxn; i++)
for(int j=0; j<maxn; j++)
a[i][j]=t[i][j];
memset(b,0,sizeof(b));
for(int i=0; i<maxn; i++)
b[i][i]=1;
while(n)
{
if(n&1) mul(b,a,b);
mul(a,a,a);
n>>=1;
}
}
void init()
{
dz[0]=dz[3]=dz[9]=dz[12]=dz[15]=1;
for(int i=0; i<16; i++)
{
for(int j=0; j<16; j++)
{
int k;
for(k=0; k<4; k++){
if((i&(1<<k))&&(j&(1<<k))) break;
}
if(k==4&&dz[i^j]==1) t[i][j]=1;
}
}
}
int main ()
{
init();
int n;
while(scanf("%d%d",&n,&mod)!=EOF)
{
if(n==0&&mod==0) break;
pow(n);
//for(int i=0; i<16; i++){for(int j=0; j<16; j++) printf("%d ",b[i][j]); printf("\n");}
printf("%d\n",b[0][0]);
}
}