蓝桥杯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 =
dp(1,i) 而 dp(1,i)=
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;
}