一、题目
赌圣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
二、递归法
1.知识点
(1) 以后只要看到出现说可能性为多少的,并且有类似于一层一层情况的,说明就是要试探每一种情况,而这种的都要想到直接用暴力递归确定可能性,即先确定第一层的情况,然后再递归寻找下一层的情况
就像这题,从上往下第一个骰子,朝上的数字有六种情况,注意要以上为准,这样才能确定下面的会不会冲突(不考虑旋转,如果考虑旋转的话,朝上数字的每一种情况都可以旋转四次,所以每一个情况都要乘以4),如果不考虑冲突的话,每一种情况对应的下一层也有六种情况,因此可以直接用递归寻找下一层,递归停止条件就是 层数为0,说明前面的n个骰子已经确定方向了
//第一层
for(int i=1;i<=6;i++){
ans=ans+f(i,n-1);//下一层,第一个参数就是像下一层说明这一层是哪面朝下,第二个参数就是说明是那一层,这是不不考虑旋转的,如果考虑的话就是要ans=ans+f(i,n-1)
}
//递归函数
f(int up,int cnt){
if(cnt==0) return 1;//不考虑旋转,说明前面的n层已经确定了一种摆放情况,如果可以旋转的话左右前后可以进行旋转四次,所以要return 4;
for(int i=1;i<=6;i++)//这一层朝上的情况
{
if(confilct[op[up]][i]==true) continue;//冲突则跳过
ans =ans+f(i,cnt-1);
}
return ans;
}
2.完整代码
import java.util.*;
public class _09垒骰子 {
static int []op=new int[7];
static boolean [][]confilct=new boolean[7][7];
static int MOD=100000007;
static void init() {
for(int i=0;i<=6;i++) {
for(int j=0;j<=6;j++) {
confilct[i][j]=false;
}
}
op[1]=4;op[2]=5;op[3]=6;
op[4]=1;op[5]=2;op[6]=3;
}
static int f(int up,int cnt) {
if(cnt==0) return 4;//因为说明前面已经确定了第n层的了,而第n层的骰子可以旋转4个方向
int ans=0;
for(int i=1;i<=6;i++) {
if(confilct[op[up]][i]==true) continue;
ans=(ans+f(i,cnt-1))%MOD;
}
return ans;
}
public static void main(String []args) {
init();
Scanner cin=new Scanner (System.in);
int n=cin.nextInt();int m=cin.nextInt();
for(int i=0;i<m;i++) {
int a=cin.nextInt();
int b=cin.nextInt();
confilct[a][b]=true;
confilct[b][a]=true;
}
int ans=0;
for(int i=1;i<=6;i++) {
ans=(ans+(4*f(i,n-1))%MOD)%MOD;//n-1层的方案数已确定,但是第1层可以每一个up可以旋转,所以每次都要乘于1个4
}
System.out.print(ans);
}
}
三、矩阵快速幂法