2015蓝桥:垒骰子(求M的k次方)(矩阵的乘法)(快速幂)

题目

垒骰子
题目描述
赌圣atm晚年迷恋上了垒骰子,就是把骰子一个垒在另一个上边,不能歪歪扭扭,要垒成方柱体。
经过长期观察,atm 发现了稳定骰子的奥秘:有些数字的面贴着会互相排斥!
我们先来规范一下骰子:1 的对面是 42 的对面是 53 的对面是 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

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。

在这里插入图片描述

深搜

package 第六届题目;

import java.util.Scanner;

//组合方案数:1.试探:暴力递归2.动态规划 3.如果有交叉子问题,记忆性递归

//递归在:1.二分查找,2.逐步生成(全排列,八零后)
//这里有,n个骰子,定义一个面朝上的数字
//对应每一种选择都往下试探,但是剪枝条件是最上面的一层不可以。
//我觉得讲的不是很专业,搜索用到递归,但笼统的说递归我感觉有点不好,之后,这题不想迷宫问题,这题没有1化多少,这题的初始就是6中情况
public class 垒骰子 {
	//预处理一个情况,面碰面的,对立面
	public static int[] op=new int[7];
	public static int n,m;
	public static long mod=1000000007;
	//存放6个面的排斥关系,只用到数组下标1~7
	//public static int a[][]=new int[7][7];
	//定义冲突数组,两两冲突
	public static boolean[][] bool=new boolean[7][7];
	//初始化
	public static void init(){
		op[1]=4;
		op[4]=1;
		op[2]=5;
		op[5]=2;
		op[3]=6;
		op[6]=3;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		init();
		n=sc.nextInt();
		m=sc.nextInt();
		for(int i=0;i<m;i++){
			int x=sc.nextInt();
			int y=sc.nextInt();
			bool[x][y]=true;//冲突了
			//二维数组,还要换一下
			bool[y][x]=true;
		}
		//结果
		long ans=0;
		//刚开始的选择
		for(int up=1;up<=6;up++){
			//乘以4是翻四个面
			ans=(ans+4*f(up,n-1))%mod;//把我定好的这一面传给你,你要判断冲突,n-1下一层
		}
		System.out.println(ans);
	}
	//上一层定好了朝上的数字为up的情况,放好cnt个骰子的方案数
	private static long f(int up, int cnt) {
		// TODO Auto-generated method stub
		long ans=0;
		if(cnt==0)
			return 4;//没有骰子出来了,因为最后//这里为什么还要乘以4???
		for(int upp=1;upp<=6;upp++){
			//如果up的反面,就是地面和upp冲突了就不可以用
			if(bool[op[up]][upp]==true&&cnt>0)
				continue;
			ans=(ans+f(upp,cnt-1))%mod;
		}
		return ans%mod;
	}

}

动态规划

在这里插入图片描述
最终得数
在这里插入图片描述

矩阵(最快求法)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package 第六届题目;

import java.util.Scanner;

public class 垒骰子最便捷的方法 {
	public static int[] op=new int[7];
	public static int n,m;
	public static long mod=1000000007;
	public static boolean[][] bool=new boolean[7][7];
	//初始化
	public static void init(){
		op[1]=4;
		op[4]=1;
		op[2]=5;
		op[5]=2;
		op[3]=6;
		op[6]=3;
	}
	//矩阵的乘法(一定要熟悉)
	static M mMultiply(M m1,M m2){
		M ans = null;
		//行乘列,再加法,三层循环
		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的k次方
	static M mPow(M m,int k){
		M ans = null;//单位矩阵
		for(int i=0;i<6;i++){
			for(int j=0;j<6;j++){
				if(i==j){
					ans.a[i][j]=1;//对角线为0;
				}else
					ans.a[i][j]=0;
			}
		}
		while(k!=0){
			if((k&1)==1){//意味着这一位的数字为1
				ans=mMultiply(ans,m);//矩阵乘法
			}
			m=mMultiply(m,m);
			k>>=1;//向右移动1位
		}
		return ans;
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		init();
		n=sc.nextInt();
		m=sc.nextInt();
		
		M cMatrix=new M();//冲突矩阵
		for(int i=0;i<m;i++){
			int x=sc.nextInt();
			int y=sc.nextInt();
			//完善冲突矩阵
			cMatrix.a[op[x]-1][y-1]=0;//输进来的是筛子的编号,但是存储从0开始
			cMatrix.a[op[y]-1][x-1]=0;
		}
		
		M cMatrix_n_1=mPow(cMatrix,n-1);//得到冲突矩阵的n-1次方
		long[][] ans=new long[6][6];
		for(int j=0;j<6;j++){
			for(int i=0;i<6;i++){
				ans=(ans+cMatrix_n_1.a[i][j])%mod;
			}
		}
		//快速幂求4的n次方
		long t=0;
		long tmp=4;
		long p=n;
		
		while(p!=0){
			if((p&1)==1)
				t=(t*t)%mod;
			tmp=(tmp*tmp)%mod;
			p>>=1;
		}
		System.out.println(t * ans %mod);
		
	}

}


//定义矩阵
class M{
	long[][] a=new long[6][6];
	public M(){
		for(int i=0;i<6;i++){
			for(int j=0;j<6;j++)
				a[i][j]=1;
		}
	}
}

1
在这里插入图片描述
第一层有34种
在这里插入图片描述
f(i)变为f(i+1)
就变为冲突表乘上(第i层k向上的方案数)
在这里插入图片描述
fi-1表示冲突矩阵乘以【111111111】。
这题演变为冲突举证的t的n-1次方,
在这里插入图片描述
最终就是t的n-1次方

正确答案

package 第六届题目;

import java.util.Scanner;

public class 垒骰子¥矩阵 {
	private static int[] op=new int[7];
	private static int n,m;
	private static long mod=1000000007;
	//public static long[][] bool=new long[7][7];
	//初始化
	static void init(){
		op[1]=4;
		op[4]=1;
		op[2]=5;
		op[5]=2;
		op[3]=6;
		op[6]=3;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		init();
		n=sc.nextInt();
		m=sc.nextInt();
		long[][] bool=new long[6][6];
		for(int i=0;i<6;i++){
			for(int j=0;j<6;j++){
				bool[i][j]=1;
			}
		}
		for(int i=0;i<m;i++){
			int x=sc.nextInt();
			int y=sc.nextInt();
			//完善冲突矩阵
			bool[op[x]-1][y-1]=0;
			bool[op[y]-1][x-1]=0;
		}
		//求冲突矩阵的n-1次方
		long[][] mPow_n_1=mPow(bool,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);
	}
	//一种方法可以放4次
	private static long power(long i, int n2) {
		// TODO Auto-generated method stub
		long ans=1;
		while(n2!=0){
			if((n2&1)==1)
				ans=(i*ans)%mod;
			i=i*i%mod;
			n2>>=1;
		}
		return ans;
	}
	private static long[][] mPow(long[][] bool, int n) {
		// TODO Auto-generated method stub
		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,bool);
			}
			//不管是不是1,都要平方,对应2进制的1,2,4,8,16
			bool=mMul(bool,bool);//自己要做一个平方
			n>>=1;
		}

		return e;
	}
	//矩阵的乘法
	private static long[][] mMul(long[][] bool2, long[][] bool3) {
		// TODO Auto-generated method stub
		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]+bool2[i][k]*bool3[k][j])%mod;//矩阵乘法
				}
			}
		}
		return ans;
	}

}


还有一种

package 第六届题目;

import java.util.Scanner;

public class 垒骰子最便捷的方法 {
	public static int[] op=new int[7];
	public static int n,m;
	private static long mod=1000000007;
	private static long[][] bool=new long[6][6];
	
	//定义矩阵
	static class Matrix{
		long[][] a=new long[6][6];
		public Matrix(){//初始化对角线元素,以构造单位矩阵
			for(int i=0;i<6;i++){
				for(int j=0;j<6;j++)
					a[i][j]=0;
			}
			for(int i=0;i<6;i++){
				a[i][i]=1;
			}
		}
	}
	//初始化
	public static void init(){
		op[1]=4;
		op[4]=1;
		op[2]=5;
		op[5]=2;
		op[3]=6;
		op[6]=3;
	}
	//一种方法可以放4次
	private static long power(long i, int n2) {
		// TODO Auto-generated method stub
		long ans=1;
		while(n2!=0){
			if((n2&1)==1)
				ans=(i*ans)%mod;
			i=i*i%mod;
			n2>>=1;
		}
		return ans;
	}	
	//求M的k次方
	static Matrix mPow(Matrix m,int k){
		Matrix ans =new Matrix();//单位矩阵
		while(k!=0){
			if((k&1)==1){//意味着这一位的数字为1
				ans=mMultiply(ans,m);//矩阵乘法
			}
			m=mMultiply(m,m);
			k>>=1;//向右移动1位
		}
		return ans;
	}
	//矩阵的乘法(一定要熟悉)
	static Matrix mMultiply(Matrix m1,Matrix m2){
		Matrix ans = new Matrix();
		//行乘列,再加法,三层循环
		for(int i=0;i<6;i++){
			for(int j=0;j<6;j++){
				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;
	}
	

	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc=new Scanner(System.in);
		init();
		n=sc.nextInt();
		m=sc.nextInt();
		Matrix cMatrix=new Matrix();//冲突矩阵
		for(int i=0;i<m;i++){
			int x=sc.nextInt();
			int y=sc.nextInt();
			//完善冲突矩阵
			cMatrix.a[op[x]-1][y-1]=0;//输进来的是筛子的编号,但是存储从0开始
			cMatrix.a[op[y]-1][x-1]=0;
		}
		
		Matrix cMatrix_n_1=mPow(cMatrix,n-1);//得到冲突矩阵的n-1次方
		//long[][] ans=new long[6][6];
		long sum=0;
		for(int j=0;j<6;j++){
			for(int i=0;i<6;i++){
				sum=(sum+cMatrix_n_1.a[i][j])%mod;
			}
		}
		System.out.println(sum*power(4,n) %mod);
		
	}

}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

向上Claire

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

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

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

打赏作者

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

抵扣说明:

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

余额充值