题目
垒骰子
题目描述
赌圣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
请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用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);
}
}