垒骰子(递归和动态规划)

垒骰子

资源限制 时间限制:1.0s 内存限制:256.0MB   

赌圣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.暴力递归(递归)上至下

#include<iostream>
#include<stdio.h>
using namespace std;
int op[7];

bool conflict[7][7];

int main(){
	op[1]=4;
	op[4]=1;
	op[2]=5;
	op[5]=2;
	op[3]=6;
	op[6]=3;
	int n,m;
	int x,y;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d",&x,&y);
		conflict[x][y]=true;
		conflict[y][x]=true;
	}
	long long dp[2][7];   //[第几层][朝上数字]  dp[][]:第几层x朝上的可能 
					//第几层用0和1轮流,因为只涉及到第n层和第n-1层(共两层)
	int cnt=0;
	
	for(int up=1;up<=6;up++){  //第一层的六种朝上情况 (初始化)
		dp[cnt][up]=1;  //up=1时有四种可能(四个侧面)up=2时有四种可能(四个侧面)...
	} 
	
	for(int level=2;level<=n;level++){
		cnt=1-cnt; //第n层时n=1
		for(int up2=1;up2<=6;up2++){  //第n层 (可想为第2层)
			 
		dp[cnt][up2]=0;		
		for(int up1=1;up1<=6;up1++){  //第n-1层 (可想为第1层)
			if(conflict[op[up2]][up1]) continue;
			dp[cnt][up2]=dp[cnt][up2]+dp[1-cnt][up1];
			
			}
		}
	}

	long long sum=0;
	for(int up2=1;up2<=6;up2++){
	sum+=dp[cnt][up2];	
	}
	
	//快速幂求n的4次方 
	 long long ans=1;
	 long long tmp=4;
	 long long p=n;
	 while(p!=0){
	 	if(p&1==1) ans=(ans*tmp)%1000000007;
	 	tmp=(tmp*tmp)%1000000007;
	 	p>>=1;
	 }
	printf("%lli",(sum*ans)%1000000007);
	
	return 0;
}

2.动态规划(递推)下至上垒
在这里插入图片描述

#include<iostream>
#include<stdio.h>
using namespace std;
int op[7];

bool conflict[7][7];

int main(){
	op[1]=4;
	op[4]=1;
	op[2]=5;
	op[5]=2;
	op[3]=6;
	op[6]=3;
	int n,m;
	int x,y;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		scanf("%d%d",&x,&y);
		conflict[x][y]=true;
		conflict[y][x]=true;
	}
	long long dp[2][7];   //[第几层][朝上数字]  dp[][]:第几层x朝上的可能 
					//第几层用0和1轮流,因为只涉及到第n层和第n-1层(共两层)
	int cnt=0;
	
	for(int up=1;up<=6;up++){  //第一层的六种朝上情况 (初始化)
		dp[cnt][up]=1;  //up=1时有四种可能(四个侧面)up=2时有四种可能(四个侧面)...
	} 
	
	for(int level=2;level<=n;level++){
		cnt=1-cnt; //第n层时n=1
		for(int up2=1;up2<=6;up2++){  //第n层 (可想为第2层)
			 
		dp[cnt][up2]=0;		
		for(int up1=1;up1<=6;up1++){  //第n-1层 (可想为第1层)
			if(conflict[op[up2]][up1]) continue;
			dp[cnt][up2]=dp[cnt][up2]+dp[1-cnt][up1];
			
			}
		}
	}

	long long sum=0;
	for(int up2=1;up2<=6;up2++){
	sum+=dp[cnt][up2];	
	}
	
	//快速幂求n的4次方 
	 long long ans=1;
	 long long tmp=4;
	 long long p=n;
	 while(p!=0){
	 	if(p&1==1) ans=(ans*tmp)%1000000007;
	 	tmp=(tmp*tmp)%1000000007;
	 	p>>=1;
	 }
	printf("%lli",(sum*ans)%1000000007);
	
	return 0;
}

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

3、用矩阵(待更新)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值