Covering(dfs打表+高斯消元+矩阵快速幂)

传送门

这是个递推题,可以看下维基的各种解释:传送门

假设递推式是一个四元方程,至于为什么也不大清楚(可能是操场是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;
}




猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/81051648