这是个递推题,可以看下维基的各种解释:传送门
假设递推式是一个四元方程,至于为什么也不大清楚(可能是操场是4*n的缘故)
然后得到递推式f(n)=f(n-1)+5*f(n-2)+f(n-3)-f(n-4)
首先使用dfs打表找出前10个数
//先使用dfs打表
#include<bits/stdc++.h>
using namespace std;
int ans=0;
bool vis[4][100];
//皮革仅有两种放法,所以一行一行进行铺
void dfs(int r,int c,int n)
{
if(r==4){//铺到最后一行,方案数++
ans++;
return;
}
//查看这个位置的下一个位置
int nc=c+1;
int nr=r;
if(nc==n){//如果这行铺到最后一列,接着查看下一行的第一个
nc=0;
nr++;
}
//如果这个位置已经被铺上,直接查看下一个位置
if(vis[r][c]){
dfs(nr,nc,n);
}else{
//如果这个位置还没有被铺上
if(c!=n-1&&!vis[r][c+1]){//横着放的情况,因为横着放最少得保证横着有两个空
vis[r][c]=vis[r][c+1]=true;
dfs(nr,nc,n);
vis[r][c]=vis[r][c+1]=false;
}
if(r!=3&&!vis[r+1][c]){//竖着放的情况,因为竖着放最少得保证竖着有两个空
vis[r][c]=vis[r+1][c]=true;
dfs(nr,nc,n);
vis[r][c]=vis[r+1][c]=false;
}
}
}
void make_table(int n)
{
for(int i=1;i<=n;i++){
ans=0;
memset(vis,false,sizeof(vis));
dfs(0,0,i);
printf("%d: %d\n",i,ans);
}
}
int main()
{
//打出前10个
make_table(10);
return 0;
}
附上一张推导的4*2的递归树
找出前10个数
然后推导这四个系数怎么出来的
因为f(n)跟f(n-1),f(n-2),f(n-3),f(n-4)有关
所以可以列出四个方程组:
A*f(4)+B*f(3)+C*f(2)+D*f(1)=f(5)
A*f(5)+B*f(4)+C*f(3)+D*f(2)=f(6)
A*f(6)+B*f(5)+C*f(4)+D*f(3)=f(7)
A*f(7)+B*f(6)+C*f(5)+D*f(4)=f(8)
所以跟据增广矩阵算出(A,B,C,D)这个向量,算这个增广矩阵跟据高斯消元法算出,最后程序采用高斯消元+完全主元素+若尔当方法
//高斯消元+完全主元素+若尔当方法
#include<bits/stdc++.h>
using namespace std;
int n;
double a[10][10];//对应的增广矩阵
double ans[10];//对应的答案
//交换行
void swap_r(int q,int p)
{
for(int i=1;i<=n+1;i++){
double t=a[p][i];
a[p][i]=a[q][i];
a[q][i]=t;
}
}
//交换列
void swap_c(int q,int p)
{
for(int i=1;i<=n+1;i++){
double t=a[i][p];
a[i][p]=a[i][q];
a[i][q]=t;
}
}
void prt()
{
for(int i=1;i<=n+1;i++){
for(int j=1;j<=n+1;j++){
printf("%2.5f ",a[i][j]);
}
printf("\n");
}
printf("\n");
}
void gs()
{
for(int i=1;i<n;i++){
//找出i i位置对应最大的主元素
double m=fabs(a[i][i]);
int p=i,q=i;
for(int j=i+1;j<=n;j++){
for(int k=i;k<=n;k++){
if(fabs(a[j][k])>m){
m=fabs(a[j][k]);
p=j;
q=k;
}
}
}
if(p!=i){
swap_r(p,i);
printf("交换%d与%d行\n",p,i);
}
if(q!=i){
swap_c(q,i);
printf("交换%d与%d列\n",q,i);
}
prt();
//把i i位置下面的进行消元
printf("进行消元\n");
for(int j=i+1;j<=n;j++){
if(a[i][j]==0.0){
continue;
}
double t=a[j][i]/a[i][i];
a[j][i]=0.0;
for(int k=i+1;k<=n+1;k++){
a[j][k]-=t*a[i][k];
}
}
prt();
}
}//此时已经换为了一个上三角形
//进行行标准型的变换
void red()
{
for(int i=n;i>1;i--){
printf("%d %d位置上方进行消元\n",i,i);
for(int j=i-1;j>=1;j--){
if(a[j][i]==0){
continue;
}
double t=a[j][i]/a[i][i];
a[j][i]=0.0;
//因为i左列的不必处理,因为i这行左边都为0
for(int k=i+1;k<=n+1;k++){
a[j][k]-=t*a[i][k];
}
}
prt();
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n+1;j++){
scanf("%lf",&a[i][j]);
}
}
//在下面增加一行1 2 3 4 0,此处在后面消元中作用大
//这个下面加了这一行最后答案进行处理,使得矩阵既可以进行行变换也可以进行列变换
for(int i=1;i<=n;i++){
a[n+1][i]=i;
}
for(int i=1;i<=n+1;i++){
for(int j=1;j<=n+1;j++){
printf("%f ",a[i][j]);
}
printf("\n");
}
printf("\n");
gs();
red();
for(int i=1;i<=n;i++){
ans[(int)a[n+1][i]]=a[i][n+1]/a[i][i];
}
for(int i=1;i<=n;i++){
printf("x%d = %f\n",i,ans[i]);
}
return 0;
}
得出递推式后,就可以进行运算了,但是这个n范围也太大了,最后就得使用矩阵快速幂,可以推出下面这个式子
把这个矩阵记作A
可以得出f(n)=A^(n-4)*(f(4),f(3),f(2),f(1))
最后附上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7;
const int N=4;
struct node
{
ll a[10][10];
};
node shu,ans,mp;
//shu是输入的矩阵,ans是所求答案
node matrix(node x,node y)
{
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++){
mp.a[i][j]=0;
for(int p=1;p<=N;p++)
mp.a[i][j]=(mp.a[i][j]+x.a[i][p]*y.a[p][j]+MOD)%MOD;
//矩阵乘法
}
return mp;
}
void work(ll k)
{
//矩阵快速幂
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
ans.a[i][j]=0;
for(int i=1;i<=N;i++) ans.a[i][i]=1;
node t=shu;
while(k){
if(k&1)
ans=matrix(ans,t);
k>>=1;
t=matrix(t,t);
}
}
int main()
{
ll n;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
shu.a[i][j]=0;
}
}
shu.a[1][1]=1;
shu.a[1][2]=5;
shu.a[1][3]=1;
shu.a[1][4]=-1;
shu.a[2][1]=1;
shu.a[3][2]=1;
shu.a[4][3]=1;
while(cin>>n)
{
if(n==1) printf("1\n");
else if(n==2) printf("5\n");
else if(n==3) printf("11\n");
else if(n==4) printf("36\n");
else
{
work(n-4);
printf("%lld\n",(ans.a[1][1]*36%MOD+ans.a[1][2]*11%MOD+ans.a[1][3]*5%MOD+ans.a[1][4])%MOD);
}
}
return 0;
}