2015蓝桥杯C++A组——垒骰子

垒骰子题目链接

问题描述

【题目描述】
在这里插入图片描述
【输入】
在这里插入图片描述
【输出】
在这里插入图片描述
【样例输入】

2 1
1 2

【样例输出】

544

题目解析

暴力法求解

假设最顶层的上面那个面是1,n表示骰子数目,调用f(1,n-1),也就是递归调用f求出剩余n-1骰子垒出的方案数,接着分别假设最顶层的上面那个面是2、3、4、5、6,同样的方法用f求出各自方案对应的剩余n-1骰子的方案数。在求解过程中利用 c o n f l i c t [ ] [ ] conflict[][] conflict[][]二维数组对当前状态加以判断,如果存在冲突就 c o n t i n u e continue continue继续执行下一种方案。注意我们考虑的所有方案数最后都要*4因为前后左右可以来回掉换位置。

用这种方法可以解决30%的问题数,对于大规模的问题求解计算量太大,我们考虑以下两种方法。

动态规划求解

动态规划的关键就是 d p [ ] [ ] dp[][] dp[][]数组的更新迭代。我们令 d p [ i ] [ j ] dp[i][j] dp[i][j]表示在第 i i i j j j在上面这个面的方案数。我们从最后一层开始递推,其表达式是 d p [ i ] [ j ] = ∑ x = 1 6 d p [ i − 1 ] [ x ] ∣ o p [ j ] 与 x 不 冲 突 dp[i][j]=\sum_{x=1}^{6}dp[i-1][x]|op[j]与x不冲突 dp[i][j]=x=16dp[i1][x]op[j]x当推到最上面一层时,直接用 a n s = ∑ x = 1 6 d p [ n ] [ x ] ans=\sum_{x=1}^{6}dp[n][x] ans=x=16dp[n][x]
在这里插入图片描述

矩阵求解

沿用上一种解法的思路,进一步优化,假设 f ( i , j ) = ∑ k = 1 6 f ( i − 1 , k ) ∣ o p [ j ] 与 k 不 冲 突 f(i,j)=\sum_{k=1}^{6}f(i-1,k)|op[j]与k不冲突 f(i,j)=k=16f(i1,k)op[j]k我们怎么才能快速的从 f 1 f_1 f1 f n f_n fn呢,假设一个列向量 [ f i − 1 , f i − 2 , . . . f i − n ] T = [ 1 , 1 , . . . 1 ] T [f_{i-1},f_{i-2},...f_{i-n}]^T=[1,1,...1]^T [fi1,fi2,...fin]T=[1,1,...1]T,以及一个冲突矩阵 T T T,以样例输入为例,1面和2面冲突,则冲突矩阵 T T T就是
[ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 ] \begin{bmatrix} &1 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \\ &1 &0 &1 &1 &1 \\ &0 &1 &1 &1 &1 \\ &1 &1 &1 &1 &1 \end{bmatrix} 111101111011111111111111111111
表示当前上面这个面为4时,则下一层的上面这个面不能为2,因为4的底面是1,1与2冲突,同理当前上面这个面为5时,下一层上面这个面不能为1。

存在这样关系 f _ n = T n − 1 × [ 1 , 1 , . . . 1 ] T f_\_n=T^{n-1}×[1,1,...1]^T f_n=Tn1×[1,1,...1]T求出的 f _ n f_\_n f_n是一个列向量,把这个列向量各个数相加就是 a n s ans ans

C++代码

暴力法代码

#include<bits/sdtc++.h>
using namespace std;
#define MOD 1000000007
int op[7]; //面对面的骰子数
bool conflict[7][7];
int n,m;
long long int f(int up,int cnt) //递归
{
    if(cnt==0) return 4; //前面n个骰子都定好了这就是4个方案
    long long ans = 0;
    for(int upp=1;upp<=6;upp++)
    {
        if(conflict[op[up]][upp]) continue;
        ans+=f(upp,cnt-1);
    }
    return ans;
}
void init()
{
    op[1]=4;
    op[4]=1;
    op[2]=5;
    op[5]=2;
    op[3]=6;
    op[6]=3;
}
int main()
{
    init();
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        conflict[x][y] = true;
        conflict[y][x] = true;
    }
    long long ans = 0;
    for(int up=1;up<=6;up++) //最上面的面的数字是up
    {
        ans = (ans+4*f(up,n-1))%MOD;
    }
    printf("%lld",ans);
    return 0;
}

dp法代码

#include<bits/stdc++.h>
using namespace std;
#define MOD 1000000007
long long dp[2][7]; //dp[i][j]表示有i层,定朝上的数字为j的稳定方案数
int n,m;
bool conflict[7][7];
map<int,int> op;
void init()
{
    op[1] = 4;
    op[4] = 1;
    op[2] = 5;
    op[5] = 2;
    op[3] = 6;
    op[6] = 3;
}
int main()
{
    init();
    scanf("%d %d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        conflict[a][b] = true;
        conflict[b][a] = true;
    }
    for(int j=1;j<=6;j++)
        dp[0][j] = 1;
    int cur = 0; //连续滚动
    for(int level=2;level<=n;level++) //迭代层数
    {
        cur = 1-cur;
        for(int j=1;j<=6;j++) //尝试将6个面放在当前一层朝上的方向
        {
            dp[cur][j] = 0;
            for(int i=1;i<=6;i++) //将与op[j]不冲突的上一层个字里面的数累加起来
            {
                if(conflict[op[j]][i]) continue;
                dp[cur][j] = (dp[cur][j]+dp[1-cur][i])%MOD;
            }
        }
    }
    long long sum = 0;
    for(int k=1;k<=6;k++)
        sum = (sum+dp[cur][k])%MOD;
    long long ans = 1; //快速幂求4的n次方
    long long tmp = 4;
    long long p = n;
    while(p!=0)
    {
        if(p&1==1) ans = (ans*tmp)%MOD;
        tmp = (tmp*tmp)%MOD;
        p>>=1;
    }
    printf("%lld",(sum*ans)%MOD);
    return 0;
}

矩阵法代码

#include<bits/stdc++.h>
using namespace std;
#define MOD 1000000007
typedef long long LL;
int n,m;
map<int,int> op;
void init()
{
    op[1] = 4;
    op[4] = 1;
    op[2] = 5;
    op[5] = 2;
    op[3] = 6;
    op[6] = 3;
}
struct M
{
    LL a[6][6];
    M()
    {
        for(int i=0;i<6;i++)
            for(int j=0;j<6;j++)
                a[i][j] = 1;
    }
};
M mMultiply(M m1,M m2)
{
    M ans;
    for(int i=0;i<6;i++)
    {
        for(int j=0;j<6;j++)
        {
            ans.a[i][j] = 0;
            for(int k=0;k<6;k++)
                ans.a[i][j]=(ans.a[i][j]+m1.a[i][k]*m2.a[k][j])%MOD;
        }
    }
    return ans;
}
M mPow(M m,int k) //求M的k次方
{
    M ans; //单位矩阵
    for(int i=0;i<6;i++)
    {
        for(int j=0;j<6;j++)
        {
            if(i==j) ans.a[i][j] = 1;
            else ans.a[i][j] = 0;
        }
    }
    while(k!=0)
    {
        if(k&1==1)
        {
            ans = mMultiply(ans,m);
        }
        m = mMultiply(m,m);
        k>>=1; //向右移动1位
    }
    return ans;
}
int main()
{
    init();
    scanf("%d %d",&n,&m);
    M cMatrix;
    for(int i=0;i<m;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        //完善冲突矩阵
        cMatrix.a[op[a]-1][b-1] = 0;
        cMatrix.a[op[b]-1][a-1] = 0;
    }
    M cMatrix_n_1 = mPow(cMatrix,n-1); //冲突矩阵的n-1次方
    LL ans = 0;
    for(int j=0;j<6;j++)
        for(int i=0;i<6;i++)
            ans = (ans+cMatrix_n_1.a[i][j])%MOD;
    LL t = 1;
    LL tmp = 4;
    LL p = n;
    while(p!=0)
    {
        if(p&1==1) t = (t*tmp)%MOD;
        tmp = (tmp*tmp)%MOD;
        p>>=1;
    }
    printf("%lld",ans*t%MOD);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芷汀若静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值