CCF CSP 拼图 c++ 100分

这个题数据给的很庞大,显然需要快速幂。

想到了状压dp,但是不会写

经过学习别人的代码,自己写了份。

每一行的状态只与前一行有关

然后每行的状态我们可以通过状压,m最大为7,以1代表该格子已经填充了,以0代表还未填充,则每行的状态有2^7种。我们预先可以找到行与行之间的转移矩阵。

通过dfs,寻找转移矩阵。now是当前行的状态,从0遍历到2^m - 1,next是下一行的状态,index是当前行,即now的第index个方格,从0开始,到m-1。当index=m时,说明当前行已经填充满,此时的next的值,代表填充完now可以转移到的状态。

以下是四种转移情况(case1-4对应代码的相应部分)

其中涉及到一些状压的位运算,比如!(next>>index&1),代表下一行的第index个放块尚未填充;next+(1<<index),代表给next的第index个方块填充上。

完整代码如下

#include<iostream>

#include<vector>

#include<string>

#include<map>

#include<set>

#include<queue>

#include<algorithm>

#include<cstdio>

#include<cstring>

using namespace std;

typedef long long ll;

typedef pair<ll,ll> pa;

const int N=(1<<7)+5,mod=1e9+7;

int w[N][N];//转移矩阵

ll n,m;

void dfs(int now,int next,int index)

{   //now是当前行的状态,next是下一行的状态,index是第几列

    if(index==m){//index从0开始,到m了说明now已经排满了

        w[now][next]++;

        return ;

    }

    if(now>>index&1)dfs(now,next,index+1);//第index个位置已经放上了,不需要再安排了

    else{

        if(index&&!(next>>index&1)&&!(next>>index-1&1))//index代表不是0,即不是最左边 case1

            dfs(now,next+(1<<index)+(1<<index-1),index+1);

        if(index<m-1&&!(next>>index&1)&&!(next>>index+1&1))//case2

            dfs(now,next+(1<<index)+(1<<index+1),index+1);

        if(index<m-1&&!(now>>index+1&1)){

            if(!(next>>index&1))dfs(now,next+(1<<index),index+2);//case3

            if(!(next>>index+1&1))dfs(now,next+(1<<index+1),index+2);//case4

        }

    }

}

void multi(int a[][N],int b[][N],int c[][N])//矩阵乘法,a*b=c

{

    static int tmp[N][N];

    memset(tmp,0,sizeof(tmp));

    for(int i=0;i<1<<m;i++)

        for(int j=0;j<1<<m;j++)

            for(int k=0;k<1<<m;k++)

                tmp[i][j]=(tmp[i][j]+(ll)a[i][k]*b[k][j])%mod;

    memcpy(c,tmp,sizeof(tmp));

}

void qpow(ll x)//快速幂,x是指数

{   

    int ans[N][N];

    memset(ans,0,sizeof(ans));

    for(int i=0;i<N;i++)ans[i][i]=1;

    while(x){

        if(x&1)multi(ans,w,ans);

        multi(w,w,w);

        x>>=1;

    }

    //ans是转移矩阵的乘法

    //初态是只有[(1<<m)-1]是1

    cout<<ans[(1<<m)-1][(1<<m)-1]<<endl;

}

int main()

{

    cin>>n>>m;

    //求出转移矩阵

    for(int now=0;now<1<<m;now++)

        dfs(now,0,0);

    qpow(n);    

    

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值