欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

蓝桥杯 15省赛 C10 垒骰子(dp数组)

程序员文章站 2022-07-13 23:53:34
...

蓝桥杯 15省赛 C10 垒骰子(dp数组)

垒骰子
赌圣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
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms

思路:
经典动态规划,马上想到dp数组

总结:
向热心大佬学习了一波 快速幂

public class 垒骰子_动态dp_10 {
	
	static int MOD =1000000007;						//由于计算过程中使用大数->需要多次取模,故声明变量;	
	static int[] numb = {-1 ,4 ,5 ,6 ,1 ,2 ,3};		//骰子接触面刚好是相对面,故不用直接用for循环变量

	static boolean[][] conf =new boolean[7][7];		//冲突
	static int n ,m;								//输入(骰子数 冲突数)
	
	public static void main(String[] args) {
		init();
		
		BigInteger[][] dp =new BigInteger[2][7];	//骰子数目可能很大,并且重复单位为2的组合(控制空间消耗),故使用大数的滚动dp数组
		int index =0;								//设置滚动数组的层指针
		
		//第一层骰子 后面就是在不冲突的对面情况下,将这个'1'一次次累加
		for(int i =1 ;i <=6 ;i ++) {dp[index][i] =BigInteger.ONE;}
		
		//第二层骰子
		for(int i =2 ;i <=n ;i ++) {
			index =1 -index;						//层指针滚动到另一层
			for(int j =1 ;j <=6 ;j ++) {
				dp[index][j] =BigInteger.ZERO;		//清空这一层数组的当前位置的值
				for(int k =1 ;k <=6 ;k ++) {
					if(!conf[numb[j]][k]) {			//上一层的这个面是否和这一层的这个面的对面冲突
						dp[index][j] =dp[index][j].add(dp[1 -index][k]).mod(new BigInteger(MOD +""));
						//每次计算尽量取模,防止数超出范围(取模运算跟乘法一样,可以分配的)
					}
				}
			}
		}
		
		//累加各个面的可能情况
		BigInteger sum =BigInteger.ZERO;
		for(int i =1 ;i <=6 ;i ++) {
			sum =sum.add(dp[index][i]).mod(new BigInteger(MOD +""));
		}
		System.out.println(sum.multiply(quickPow(4 ,n)).mod(new BigInteger(MOD +"")));
		//侧面有4种朝向,故对'4'取幂
	}

	//快速幂
	private static BigInteger quickPow(int x, int y) {
		
		BigInteger ans =BigInteger.ONE;
		BigInteger base =new BigInteger(x +"");	//当前底数(不是这里的'x',会累积的)
		
		while(y !=0) {
			if((y &1) ==1) {							
				//指数的二进制的末位是'1'的话,乘上当前权值(权值:底数的当前指数次方)
				ans =ans.multiply(base).mod(new BigInteger(MOD +""));
			}
			base =base.multiply(base).mod(new BigInteger(MOD +""));//底数
			y >>=1;//指数右移(不是减一)
		}
		
		return ans;
	}

	private static void init() {
		
		Scanner sc =new Scanner(System.in);
		
		n =sc.nextInt();
		m =sc.nextInt();
		for(int i =0 ;i <m ;i ++) {
			int a =sc.nextInt();
			int b =sc.nextInt();
			
			conf[b][a] =conf[a][b] =true;
		}
		
		sc.close();
	}

}
相关标签: 蓝桥