蓝桥杯垒骰子java_java实现第六届蓝桥杯垒骰子

< 垒骰子

题目描述

赌圣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,否则按无效代码处理。

解法一:

public class 垒骰子_9_滚动数组 {

private static int a[][] = new int[10][10];//存放6个面的排斥关系,只用到数组下标1~7

private static int b[] = new int [7];//对立面

private static long count ;

private static long C = 1000000007;

private static boolean check(int current,int last)

{

if(a[current][last]==1)//说明两个骰子互相排斥

{

return true;

}

return false;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

b[1]=4;b[4]=1;

b[2]=5;b[5]=2;

b[3]=6;b[6]=3;

int n,m,a1,a2;

Scanner in = new Scanner(System.in);

n = in.nextInt();

int num = 4;

m = in.nextInt();

for(int i = 0;i

{

a1 = in.nextInt();

a2 = in.nextInt();

a[a1][a2]=1;

a[a2][a1]=1;

}

//滚动数组

int dp[][] = new int[2][7];//dp[i][j]表示某一高度的骰子j面朝上的方案数

int e = 0;

for(int i=1;i<7;i++)

{

dp[e][i]=1;//初始化 已知高度为1的骰子的方案只有一种,此处忽略结果×4的情况,在下面加上

}

for(long i=2;i<=n;i++)//从第二颗骰子算,到第n颗

{

e = 1-e;

num = (int) ((num*4)%C);

for(int j = 1;j<7;j++)//遍历当前骰子各面

{

dp[e][j]=0;//初始化下一颗骰子j面朝上的方案数为0

for(int k = 1;k<7;k++)//遍历之前一颗骰子的6个面

{

if(!check(k,b[j]))//不相互排斥,k为之前下方骰子的朝上面,b[j]为当前骰子朝上面的对立面,即朝下面

{

dp[e][j] += dp[1-e][k];

}

}

dp[e][j] = (int) (dp[e][j]%C);

}

}

for(int i = 1;i<7;i++)

{

count += dp[e][i];

count = count%C;

}

count = (count*num)%C;//这个地方相乘后仍然很大,是这个算法的弊端

//count = quickPow(10,33,1000000007);

System.out.println(count);

}

//整数快速幂,写在这里只是为了加强记忆,这个地方没用,为后续快速矩阵幂解法做铺垫

private static long quickPow(long count2,int n,long mod)

{

long res = count2;

long ans = 1;

while(n!=0)

{

if((n&1)==1)

{

ans = (ans*res)%mod;

}

res = (res*res)%mod;

n >>= 1;

}

return ans;

}

}

解法二:

此篇java代码实现了快速矩阵幂来计算前n-1个6*6阶矩阵的乘积,最后的sum相当于传送门里博主的B矩阵求和,也就是最终没有乘4n的答案,这样就得到了第n个骰子各面朝上的所有情况,当然要记得最后乘个4n,在这里顺便也给出了整数快速幂的实现。

public class 垒骰子_9_快速矩阵幂 {

private static int mod = 1000000007;

static class Matrix

{

int a[][]= new int [6][6];

public Matrix(){}

public Matrix(int x)//初始化对角线元素,以构造单位矩阵

{

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] = x;

}

}

}

public static int q_pow(int m,int n,int mod)//计算m^n

{

int base = m;

int ans = 1;

while(n>0)

{

if((n&1)==1)

ans = (ans*base)%mod;

base = (base*base)%mod;

n>>=1;

}

return ans;

}

public static Matrix mul(Matrix m1,Matrix m2)

{

Matrix m = new Matrix();

for(int i = 0;i<6;i++)

{

for(int j = 0;j<6;j++)

{

for(int k = 0;k<6;k++)

{

m.a[i][j] += (m1.a[i][k]*m2.a[k][j])%mod;

}

}

}

return m;

}

public static Matrix q_pow(Matrix m,int n)

{

Matrix ans = new Matrix(1);//这里要变成单位矩阵

Matrix base = m;

while(n>0)

{

if((n&1)==1)

ans = mul(ans,base);

base = mul(base,base);

n>>=1;

}

return ans;

}

public static void main(String[] args) {

// TODO Auto-generated method stub

int n,m,a1,a2;

int sum = 0;

Scanner in = new Scanner(System.in);

n = in.nextInt();

int num;

m = in.nextInt();

Matrix matrix = new Matrix();

for(int i = 0;i<6;i++)

{

for(int j=0;j<6;j++)

{

matrix.a[i][j]= 1;

}

}

for(int i = 0;i

{

a1 = in.nextInt();

a2 = in.nextInt();

matrix.a[a1-1][a2-1]=0;

matrix.a[a2-1][a1-1]=0;

}

//快速矩阵幂运算

Matrix final_matrix = q_pow(matrix,n-1);

for(int i=0;i<6;i++)

{

for(int j=0;j<6;j++)

{

sum = (sum+final_matrix.a[i][j])%mod;

}

}

num = q_pow(4,n,mod);

System.out.println((sum*num)%mod);

}

}————————————————

原文链接:https://blog.csdn.net/a1439775520/article/details/97634322

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值