垒骰子【蓝桥杯】

赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。
假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。 
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 10^9 + 7 的结果。


不要小看了 atm 的骰子数量哦~


「输入格式」
第一行两个整数 n m
n表示骰子数目
接下来 m 行,每行两个整数 a b ,表示 a 和 b 数字不能紧贴在一起。
「输出格式」
一行一个数,表示答案模 10^9 + 7 的结果。
「样例输入」
2 1
1 2
「样例输出」
544
————————————————

「数据范围」
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 10^9, m <= 36

考察点:递归或者递推(动态规划

package pastExamPaper;

import java.math.BigInteger;
import java.util.Scanner;

/*
    垒骰子(两种方法:递归或者动态规划【递推】)
 */
public class Main {
    private static int[] op = new int[7];
    private static long[][] conflict = new long[7][7];
    private static final long MOD = 1000000007;
//    private static BigInteger MOD = new BigInteger(Math.pow(10,9));
//    private static int ans;
    public static void main(String[] args) {
        op[1] = 4;
        op[2] = 5;
        op[3] = 6;
        op[4] = 1;
        op[5] = 2;
        op[6] = 3;
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();

        for ( int i = 0; i < 6; i++ ){
            for ( int j = 0; j < 6; j++ ){
                conflict[i][j] = 1;
            }
        }

        //建立冲突矩阵
        for ( int i = 0; i < m; i++ ){
            int a = sc.nextInt();
            int b = sc.nextInt();
            conflict[op[a]-1][b-1] = 0;
            conflict[op[b]-1][a-1] = 0;
        }
        //求冲突矩阵的n-1次方
        long[][] mPow_n_1 = mPow(conflict,n-1);


        //累加矩阵的每个元素
        long ans = 0;
        for ( int i = 0; i < 6; i++ ){
            for ( int j = 0; j < 6; j++ ){
                ans = (ans + mPow_n_1[i][j])%MOD;
            }
        }

        //求ans*4^n
        System.out.println(ans*power(4,n)%MOD);

//        int ans = 0;
//        for ( int up = 1; up <= 6; up++ ){
//            ans = (ans + f(up,n-1));
//        }
//        System.out.println(ans);
    }

    private static long power(long i, int n) {
        long ans = 1;
        while (n!=0){
            if ((n&1)==1) ans = (ans*i)%MOD;
            i = i*i^MOD;
            n>>=1;
        }
        return ans;
    }

    //矩阵额快速幂
    private static long[][] mPow(long[][] conflict, int n) {
        long[][] e = new long[6][6];

        for ( int i = 0; i < 6; i++ ){
            for ( int j = 0; j < 6; j++ ){
                if ( i == j ) e[i][j] = 1;
                else e[i][j] = 0;
            }
        }

        while (n!=0){
            if ((n&1) == 1){
                e = mMul(e,conflict);
            }
            conflict = mMul(conflict,conflict);
            n>>=1;
        }

        return e;
    }
    


    private static long[][] mMul(long[][] a, long[][] b) {

        long[][] ans = new long[6][6];
        for ( int i = 0; i < 6; i++ ){
            for ( int j = 0; j < 6; j++ ){
                for ( int k = 0; k < 6; k++ ){
                    ans[i][j] = (ans[i][j] + a[i][k]*b[k][j])%MOD;
                }
            }
        }

        return ans;
    }

//    //递归
//    private static int f(int up, int cnt){
//        //出口
//        if (cnt == 0){
//            return 4;
//        }
//        int ans = 0;
//        for ( int upp = 1; upp <= 6; upp++ ){
//            if (conflict[op[up]][upp]) continue;
//            ans = ans + 4*f(upp,cnt - 1);
//        }
//        return ans;
//    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值