hdu 6185 Covering

题目链接

看到题就想起以前做过的类似多米诺骨牌相关的题目,想着应该是有递推式的,菜鸟的我就开始画图,画到废了废了,之后借助网上大佬博客的递推演算:

 

行数为4
当n=2时,方案数为5;
当n>2时,方案数可由5种情况组合:
 第一种情况(第一列填充满):     num1=f[n-1];
 第二种情况(前两列填充满):     num2=f[n-2];
 第三种情况(根据列填充满判断): num3=f[n-2]+f[n-3]+……+f[0];
 第四种情况同第三种:          num4=num3;
 第五种情况判断方法同第三种:   num5=f[n-2]+f[n-4]+……+f[0]/f[1];

所以:f[n]=num1+num2+num[3]+num[4]+num[5];
因为第五种中存在两种情况,为了避免,采取f[n]-f[n-2],整理可得:
f[n]=f[n-1]+5f[n-2]+f[n-3]-f[n-4];
再利用矩阵快速模幂求解,注意负数模幂先加上mod

1≤n≤10181

/*矩阵快速幂*/
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=4;

struct Matrix{//矩阵结构体
	ll a[16][16];
		void init(){ //初始化为单位矩阵 
		memset(a,0,sizeof(a));
		for(int i=0;i<N;i++)
			a[i][i]=1;
	}
};
Matrix g,ans,tmp;

Matrix matrix(Matrix x,Matrix y){//矩阵乘法 
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++){
            tmp.a[i][j]=0;
            for(int k=0;k<N;k++)
                tmp.a[i][j]=(tmp.a[i][j]+x.a[i][k]*y.a[k][j]+mod)%mod;  //+mod 负数取模 
        }
    return tmp;
}
void qpow(ll n){//矩阵快速幂 
	ans.init();
	Matrix t=g;
    while(n){
        if(n&1) 
            ans=matrix(ans,t);
        n>>=1;
        t=matrix(t,t);
    }
}
int main()
{
	ll n;
	while(~scanf("%lld",&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 
		{
			memset(g.a,0,sizeof(g.a));
			g.a[0][0]=1; g.a[0][1]=5; g.a[0][2]=1; g.a[0][3]=-1;
			g.a[1][0]=1;			  
						 g.a[2][1]=1;		   
									  g.a[3][2]=1;
			
			qpow(n-4);
			printf("%lld\n",(ans.a[0][0]*36l%mod+ans.a[0][1]*11l%mod+ans.a[0][2]*5l%mod+ans.a[0][3])%mod);
		}
	}
	return 0;
}

可以借助暴力搜索得出前几个数据再进行找规律(这个提交会TL)

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int mp[15][15];
int vis[15][15];
int n;
ll sum=0;
int cnt=0;

bool safe(int x, int y) {  //是否越界 
    if (x>=0&&x<4&&y>=0&&y<n&&!vis[x][y])  return 1;
    return 0;
}
bool check() {  //是否结束 
    for (int i=0; i<4; i++) {
        for (int j=0; j<n; j++) {
            if (!vis[i][j])  {
                return 0;
            }
        }
    }
    return 1;
}
int findx() {  //找未搜索点的横坐标 
    for (int i=0; i<4; i++) {
        for (int j=0; j<n; j++) {
            if (!vis[i][j])  {
                return i;
            }
        }
    }
    return -1;
}
int findy() {  //找未搜索点的纵坐标 
    for (int i=0; i<4; i++) {
        for (int j=0; j<n; j++) {
            if (!vis[i][j])  {
                return j;
            }
        }
    }
    return -1;
}

void dfs(int x, int y) {
    for (int k=0; k<2; k++) {
        if (k==0) { //用2*1规格的,标记为2 
            if(safe(x,y)&&safe(x+1,y)) {  //范围内 
                vis[x][y]=2, vis[x+1][y]=2;
                if (check())  {  //搜索结束 
                    sum++;
                    vis[x][y]=0, vis[x+1][y]=0;
                }
                else {  //未结束,继续搜索 
                    int xx=findx(), yy=findy();
                    if(xx!=-1&&yy!=-1) {
                        vis[x][y]=2, vis[x+1][y]=2;
                        dfs(xx,yy);
                        vis[x][y]=0, vis[x+1][y]=0;
                    }
                }

            }
        }
        if (k==1) {   //用1*2规格的,标记为1 
            if (safe(x,y)&&safe(x,y+1)) {
                vis[x][y]=1, vis[x][y+1]=1;
                    if (check())  {
                        sum++;
                        vis[x][y]=0, vis[x][y+1]=0;
                    }
                    else {
                            vis[x][y]=1, vis[x][y+1]=1;
                        int xx=findx(), yy=findy();
                        if(xx!=-1&&yy!=-1) {
                            dfs(xx,yy);
                            vis[x][y]=0, vis[x][y+1]=0;
                        }
                    }
            }
        }
    }
    
}

int main() {
	while(~scanf("%d",&n)){
		sum=0,cnt=0;
		memset(vis, 0, sizeof(vis));
	    dfs(0, 0);
	    printf("%lld\n",sum); 
	}
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值