蓝桥杯2015c++A组真题&代码第九题垒骰子

蓝桥杯2015c++A组真题&代码第九题垒骰子

/*
垒骰子

赌圣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


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。


*/

 这题可以类比数位dp,不过比数位dp更加简单一些,通过对有向无环图的建立,我们可以使用动态规划进行计算

假定从第一个骰子开始计算,求所有值就是 求 从第一个骰子开始,每一个面的数量

那么 sum =  \sum_{i=1}^{6}dp(1,i)  

而 dp(1,i)=\sum_{k=1}^{6}dp(2,k) | i 和 k不冲突  的所有值,对应层层递归,就是我们要的答案,这里可以理解为有向无环图的寻找,对于每一层i

都有对应的状态路径可以走,而这里有对应的6个状态可以选择,表示每个朝上的面,是什么数字。

#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm> 
using namespace std;
typedef long long ll; 
typedef vector<ll>  vec;
typedef vector<vec> mat;


const int MAXN = 1e9+10;
int N,M;
int back[7];
bool conf[7][7];
int dp[2][7];
const int mod = 1e9+7;
void init(){
	back[1] = 4;back[2] = 5;back[3] = 6;
	back[4] = 1;back[5] = 2;back[6] = 3;
}

int f(int step,int num){
	if(dp[step][num]!=-1) return dp[step][num];
	if(step>=N) return 1;
	int res =0;
	for(int i=1;i<=6;i++){
		if(conf[i][back[num]]) continue;
		res= (res%mod+ 4*f(step+1,i)%mod)%mod;
	}
	return dp[step][num] = res;
}

int fun(){
	int res=0;
	for(int i=1;i<=6;i++){
		res = (res%mod+ 4*f(1,i)%mod)%mod;
	}
	
	return res;
}


int dp_f(){
	int *crt = dp[0]; int *next = dp[1];
	
	for(int i=1;i<=6;i++) crt[i] = 1;
	for(int i=2;i<=N;i++){
		for(int j=1;j<=6;j++){
			next[j] = 0;
			for(int k=1;k<=6;k++){
				if(conf[k][back[j]]) continue;
				next[j] = (next[j]+4*crt[k])%mod;	
			}
		}
		swap(next,crt);
	}
	int res = 0;
	for(int i=1;i<=6;i++){
		res=(res+ 4*crt[i])%mod; 
	}
	return res; 
}

mat mul(mat &a, mat &b,ll mod){
	mat c(a.size(),vec(b[0].size()));
	for(int i=0;i<a.size();i++){
		for(int j=0;j<b[0].size();j++){
			for(int k =0;k<b.size();k++){
				if(mod > 0)
				c[i][j] = (c[i][j]%mod + 4*a[i][k]*b[k][j]%mod) %mod;
				else
				c[i][j] = (c[i][j] + 4*a[i][k]*b[k][j]);
			}
		}
	}
	return c;
}

mat pow_m(mat a,ll n,ll mod){
	mat b(a.size(),vec(a[0].size()));
	for(int i=0;i<a.size();i++){
		b[i][i] = 1;
	}
	while(n>0){
		if(n&1) b = mul(b,a,mod);
		a = mul(a,a,mod);
		n>>=1;
	}
	return b;
}

ll solve(){
	mat a(6,vec(1));
	for(int i=0;i<a.size();i++){
		a[i][0] = 1;
	}
	mat map(6,vec(6));
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			if(!conf[back[i+1]][j+1])
				map[i][j] = 1;
		}
	}
//	for(int i=0;i<6;i++){
//		for(int j=0;j<6;j++){
//			printf("%d ",map[i][j]) ;
//		}
//		printf("\n");
//	}
//	
	 
	mat res = pow_m(map,N-1,mod);
	res = mul(res,a,mod); 
	ll ans = 0;
	for(int i=0;i<a.size();i++){
		ans = (ans+ res[i][0])%mod ;
	}
	
	return ans;
}

int main(){
	init();
	memset(dp,-1,sizeof(dp));
	memset(conf,0,sizeof(conf));
	scanf("%d%d",&N,&M);
	for(int i=0;i<M;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		conf[a][b] = true;
		conf[b][a] = true;
	}
	int res=0;
	//记忆化搜索 
//	res = fun();
	//动态规划 
//	res = dp_f();
	//矩阵计算 
	res = solve();
	printf("%d\n",res);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Willen_/article/details/88608438