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

蓝桥杯 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 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数组
		//第一层骰子 后面就是在不冲突的对面情况下,将这个'1'一次次累加
		for(int i =1 ;i <=6 ;i ++) {dp[0][i] =BigInteger.ONE;}
		
		//第二层骰子
		int index =0;								//设置滚动数组的层指针
		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]) {			//上一层的这个面是否和这一层的这个面的对面冲突
						if(!conf[j][k]) {			//上一层的这个面是否和这一层的这个面的对面冲突
						dp[index][j] =dp[index][j].add(dp[1 -index][k]).mod(new BigInteger(MOD +""));
						//每次计算尽量取模,防止数超出范围(取模运算跟乘法一样,可以分配的)
					}
				}
			}
		}
		
		//累加各个面的可能情况
		for(int i =2 ;i <=6 ;i ++) {
			dp[index][1] =dp[index][1].add(dp[index][i]).mod(new BigInteger(MOD +""));
		}
		System.out.println(dp[index][1].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();
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_43638238/article/details/107698755