蓝桥杯垒筛子C语言

赌圣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状态转移,而若是一个一个筛子进行状态转移在n的数特别大的时候则会运行超时,所以我们多个筛子之间进行状态转移,就好像如果我要算2的10^9次方时,我则会将10^9进行分解,如果可以除二则除二,否则减一,直到数变成1为止,用一个数组来记录操作步骤,同样的我们将筛子数量也进行相同的分解,把乘法变为dp状态转移的操作即可。

dp状态转移的骰子组分为36个情况,骰子组的头部有1到6的情况,尾部也是1到6,所以有6×6=36种,每一次拼接便是把两组骰子的所有情况进行判定:是否可以进行拼接,如果可以便把两边骰子组的当前情况的所有可能数进行相乘。

#include<stdio.h>

void yiban(long long a[][6], long long c[][6])//相同数量的骰子组之间进行拼接
{
	long long z[6][6];
	int i, j, ii, jj;
	for (i = 0; i < 6; i++)
	{
		for (j = 0; j < 6; j++)
		{
			z[i][j] = c[i][j]; c[i][j] = 0;
		}
	}
	for (i = 0; i < 6; i++)
	{
		for (j = 0; j < 6; j++)
		{
			for (ii = 0; ii < 6; ii++)
			{
				for (jj = 0; jj < 6; jj++)
				{
					if (a[j][ii] == 0 && z[i][j] != 0 && z[ii][jj] != 0)
					{
						c[i][jj] = (c[i][jj] + z[i][j] * z[ii][jj]) % 1000000007;
					}
				}
			}
		}
	}
}//i、j代表上面的骰子组,ii、jj代表下面的骰子组,下面的yige函数同理

void yige(long long a[][6], long long b[][6], long long c[][6])/*与只有一个骰子的骰子组进行拼接*/
{
	long long z[6][6];
	int i, j, ii, jj; 
	for (i = 0; i < 6; i++)
	{
		for (j = 0; j < 6; j++)
		{
			z[i][j] = c[i][j]; c[i][j] = 0;
		}
	}
	for (i = 0; i < 6; i++)
	{
		for (j = 0; j < 6; j++)
		{
			for (ii = 0; ii < 6; ii++)
			{
				for (jj = 0; jj < 6; jj++)
				{
					if (a[j][ii] == 0 && z[i][j] != 0 && z[ii][jj] != 0)
					{
						c[i][jj] = (c[i][jj] + b[ii][jj] * z[i][j]) % 1000000007;
					}
				}
			}
		}
	}
}

int main()
{
	int i, j;
	long long n, m, a[6][6] = { 0 }, b[6][6] = { 0 }, aa, bb, c[6][6] = { 0 }, d[1000] = { 0 }, q;/*d数组记录分解步骤,a数组记录不可对面,b数组代表单个骰子的的所有可能,c数组代表当前最大骰子组的所以可能*/
	for (i = 0; i < 6; i++)
	{
		for (j = 0; j < 6; j++)
		{
			a[i][j] = 0; b[i][j] = 0; c[i][j] = 0;
		}
	}//数组初始化现在看好像没什么用
	scanf("%lld%lld", &n, &m);
	for (i = 0; i < m; i++)
	{
		scanf("%lld%lld", &aa, &bb);
		a[aa - 1][bb - 1] = 1; a[bb - 1][aa - 1] = 1;
	}//a数组的记录
	b[0][3] = 4; b[1][4] = 4; b[2][5] = 4; b[3][0] = 4; b[4][1] = 4; b[5][2] = 4;
	c[0][3] = 4; c[1][4] = 4; c[2][5] = 4; c[3][0] = 4; c[4][1] = 4; c[5][2] = 4;//b、c数组的初始化,此时都为一个骰子
	for (q = 0; n != 1; q++)
	{
		if (n % 2 == 0)
		{
			n /= 2; d[q] = 2;
		}
		else
		{
			n -= 1; d[q] = 1;
		}
	}//d数组的记录
	q--;
	for (q; q >= 0; q--)
	{
		if (d[q] == 2) yiban(a, c);
		else if (d[q] == 1) yige(a, b, c);
	}
	q = 0;
	for (i = 0; i < 6; i++)
	{
		for(j = 0; j < 6; j++)
		{
			q = (q + c[i][j]) % 1000000007;
		}
	}//将最后得到的c数组的所有面的所有可能相加既是最终答案
	printf("%lld", q);
	return 0;
}

本人知识欠缺,若有不对或者与其他作者过于相同,敬请原谅。(狗头保命)

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值