蓝桥杯之垒骰子

题目描述
赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。

经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!

我们先来规范一下骰子:1 的对面是 4,2 的对面是 5,3 的对面是 6。

假设有 m 组互斥现象,每组中的那两个数字的面紧贴在一起,骰子就不能稳定的垒起来。
atm想计算一下有多少种不同的可能的垒骰子方式。
两种垒骰子方式相同,当且仅当这两种方式中对应高度的骰子的对应数字的朝向都相同。
由于方案数可能过多,请输出模 10^9 + 7 的结果。
不要小看了 atm 的骰子数量哦~

示例
输入第一行两个整数 n m;
n表示骰子数目;
接下来 m 行,每行两个整数 a b ,表示 a 和 b 数字不能紧贴在一起。
对于 30% 的数据:n <= 5
对于 60% 的数据:n <= 100
对于 100% 的数据:0 < n <= 10^9, m <= 36

例如:
用户输入:
2 1
1 2
输出一行一个数,表示答案模 10^9 + 7 的结果。
例如:
对应输出:
544

暴力解法(dfs):

import java.util.Scanner;
public class LanQiao36 {
    
    
    static int res = 0;
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();// n个骰子
        int m = sc.nextInt();// m对互斥关系
        int[][] nums = new int[m][2];// 存储每一组互斥关系
        for(int i = 0; i < m; i++){
    
    
            for(int j = 0; j < 2; j++){
    
    
                nums[i][j] = sc.nextInt();
            }
        }
        int[] dianShu = {
    
    1, 2, 3, 4, 5, 6};
        dfs(n, nums, dianShu, 0, 1);
        long ans = (long)(res * Math.pow(4, n)) % 1000000007;
        System.out.println(ans);
    }
    public static void dfs(int n, int[][] nums, int[] dianShu, int k, int temp){
    
    //temp 存储上一个骰子的顶面点数是多少
        if(k == n){
    
    
            res++;
            return;
        }
        for(int i = 0; i < dianShu.length; i++){
    
    
            //当 k 大于等于 1 的时候,即到了第二个骰子的时候才用和上一个骰子比较是否出现互斥,只有第一个骰子是没办法比的
            //第一次做的时候没想出来如何实现以上思路,但后来发现只要加上 k >= 1就行
            if(k >= 1){
    
    
                if(isValid(nums, temp, getDuiLi(dianShu[i]))){
    
    
                    dfs(n, nums, dianShu, k + 1, dianShu[i]);
                }
            }else{
    
    
                dfs(n, nums, dianShu, k + 1, dianShu[i]);
            }
        }
    }
    public static boolean isValid(int[][] nums , int x, int y){
    
    //判断是否互斥
        for(int[] arr : nums){
    
    
            if((arr[0] == x && arr[1] == y) || (arr[0] == y && arr[1] == x)){
    
    
                return false;
            }
        }
        return true;
    }
    public static int getDuiLi(int num){
    
    //取得一个骰子上面点数的对立面的点数
        if(num <= 3){
    
    
            return num + 3;
        }
        return num - 3;
    }
}

将判断是否互斥的语句改进一下:

import java.util.Scanner;

public class LanQiao37 {
    
    
    static int res = 0;
    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();// n个骰子
        int m = sc.nextInt();// m对互斥关系
        boolean[][] nums = new boolean[6][6];// 存储每一组互斥关系,i代表第一个骰子,j代表第二个骰子
        for (int i = 0; i < m; i++) {
    
    
            int num1 = sc.nextInt();
            int num2 = sc.nextInt();
            //注意:都要赋成true
            nums[num1 - 1][num2 - 1] = true;
            nums[num2 - 1][num1 - 1] = true;
        }
        int[] dianShu = {
    
    1, 2, 3, 4, 5, 6};
        dfs(n, nums, dianShu, 0, 1);
        long ans = (long) (res * Math.pow(4, n)) % 1000000007;
        System.out.println(ans);
    }

    /**
     * @param n       n个骰子
     * @param nums    存储第一个骰子和第二个骰子互斥的点数
     * @param dianShu 骰子的点数
     * @param k       记录是第几个骰子
     * @param temp    存储上一个骰子的顶面点数是多少
     */
    public static void dfs(int n, boolean[][] nums, int[] dianShu, int k, int temp) {
    
    
        if (k == n) {
    
    
            res++;
            return;
        }
        for (int i = 0; i < dianShu.length; i++) {
    
    
            //当 k 大于等于 1 的时候,即到了第二个骰子的时候才用和上一个骰子比较是否出现互斥,只有第一个骰子是没办法比的
            //第一次做的时候没想出来如何实现以上思路,但后来发现只要加上 k >= 1就行
            if (k >= 1) {
    
    
                if (!nums[temp - 1][getDuiLi(dianShu[i]) - 1]) {
    
    
                    dfs(n, nums, dianShu, k + 1, dianShu[i]);
                }
            } else {
    
    
                dfs(n, nums, dianShu, k + 1, dianShu[i]);
            }
        }
    }

    public static int getDuiLi(int num) {
    
    //取得一个骰子上面点数的对立面的点数
        if (num <= 3) {
    
    
            return num + 3;
        }
        return num - 3;
    }
}

动态规划法:

import java.util.Scanner;

public class LanQiao35 {
    
    
    private static int[][] a = new int[7][7];//存放6个面的排斥关系,只用到数组下标1~6
    private static long count;
    private static final long C = 1000000007;

    public static void main(String[] args) {
    
    
        int num = 4;
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for (int i = 0; i < m; i++) {
    
    
            int a1 = sc.nextInt();
            int a2 = sc.nextInt();
            a[a1][a2] = 1;
            a[a2][a1] = 1;
        }
        //滚动数组(只用了两层)
        int dp[][] = new int[2][7];//dp[i][j]表示某一高度的骰子j面朝上的方案数
        int e = 0;
        for (int i = 1; i < 7; i++) {
    
    
            dp[e][i] = 1;//初始化 已知高度为1的骰子的方案只有一种,此处忽略结果×4的情况,在下面加上
        }
        for (int i = 2; i <= n; i++) {
    
    //从第二颗骰子算,到第n颗
            e = 1 - e;
            num = (int) ((num * 4) % C);
            for (int j = 1; j < 7; j++) {
    
    //遍历当前骰子各面
                dp[e][j] = 0;//初始化下一颗骰子j面朝上的方案数为0
                for (int k = 1; k < 7; k++) {
    
    //遍历之前一颗骰子的6个面
                    if (!isValid(k, getDuiLi(j))) {
    
    //不相互排斥,k为之前下方骰子的朝上面,getDuiLi(j)为当前骰子朝上面的对立面,即朝下面
                        dp[e][j] += dp[1 - e][k];
                    }
                }
                dp[e][j] = (int) (dp[e][j] % C);
            }
        }
        for (int i = 1; i < 7; i++) {
    
    
            count += dp[e][i];
            count = count % C;
        }
        count = (count * num) % C;//这个地方相乘后仍然很大,是这个算法的弊端
        //count = (count * Math.pow(4, n)) % C;
        System.out.println(count);
    }

    public static int getDuiLi(int num) {
    
    //取得一个骰子上面点数的对立面的点数
        if (num <= 3) {
    
    
            return num + 3;
        }
        return num - 3;
    }

    private static boolean isValid(int current, int last) {
    
    
        if (a[current][last] == 1) {
    
    //说明两个骰子互相排斥
            return true;
        }
        return false;
    }
}

非滚动数组:

import java.util.Scanner;

public class LanQiao34_05 {
    
    
    private static int[][] a = new int[7][7];//存放6个面的排斥关系,只用到数组下标1~6
    private static long count;
    private static final long C = 1000000007;

    public static void main(String[] args) {
    
    
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for (int i = 0; i < m; i++) {
    
    
            int a1 = sc.nextInt();
            int a2 = sc.nextInt();
            a[a1][a2] = 1;
            a[a2][a1] = 1;
        }
        int num = 4;
        int[][] dp = new int[n + 1][7];
        for(int j = 1; j < 7; j++){
    
    
            dp[1][j] = 1;
        }
        for(int i = 2; i <= n; i++){
    
    
            num = (num * 4) % (int)C;
            for(int j = 1; j < 7; j++){
    
    //遍历当前骰子的 1 到 6 的点数
                for(int k = 1; k < 7; k++){
    
    //遍历上一个骰子的 1 到 6 的点数
                    if(!isValid(getDuiLi(j), k)){
    
    
                        dp[i][j] += dp[i - 1][k];
                    }
                }
                dp[i][j] = dp[i][j] % (int)C;
            }
        }
        for(int j = 1; j < 7; j++){
    
    
            count += dp[n][j];
            count = count % C;
        }
        count = (count * num) % C;//这个地方相乘后仍然很大,是这个算法的弊端
        //count = (count * Math.pow(4, n)) % C;
        System.out.println(count);
    }

    public static int getDuiLi(int num) {
    
    //取得一个骰子上面点数的对立面的点数
        if (num <= 3) {
    
    
            return num + 3;
        }
        return num - 3;
    }

    private static boolean isValid(int current, int last) {
    
    
        if (a[current][last] == 1) {
    
    //说明两个骰子互相排斥
            return true;
        }
        return false;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_46497503/article/details/113032341