原题链接:1217. 垒骰子 - AcWing题库
题意:垒骰子,一个一个往上加,给定某些组合认为叠放在一起不稳定,给定骰子数,问一共有多少种方案
思路
DP
大概是可以看出DP的思路的 求方案数,前后动态相互影响
状态表示
第n次时,选定这一次朝上的面(1-6)后的方案数
状态计算
分类为选定的面,选定好这一次的数字后,方案数为上一次的不同面的方案数加和
但是计算要遍历n,题目给定的 n 为 ,必超时
矩阵乘法
将DP思想运用在矩阵乘法,想到只有6种分类,可以用矩阵存下这六种类别,同时存下这次选了不同的数的对应结果 即行为这一次选的数 列为前一次不同选法后,这一次选了行代表的数的可能方案数
而DP关系式可以实现为 定义可选与否矩阵 含义是这一次选了这个数后,下一次可以选的数,可选为1,不可选为0
二者相乘可以得到 为 第 n+1 个骰子选定的不同可能与对应的最大方案数
快速幂加取模
注意
- 该题认为侧向的四个面顺序不同为不同方案
- 每次选定的数是朝上的数,因为骰子面与面的对应关系,选定朝上的数就确定了朝下的数,要做一个映射关系
实现
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 7, mod = 1e9 + 7;
LL a[N][N];
LL f[N][N];
LL ans;
int n, m;
int r[N] = {0, 4, 5, 6, 1, 2, 3};
void mul(LL a[][7], LL b[][7])
{
LL c[N][N] = {};
for(int i = 1; i <= 6; i ++)
for(int j = 1; j <= 6; j ++)
for(int k = 1; k <= 6; k ++)
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % mod;
memcpy(a, c, sizeof c);
}
void qmi(int n)
{
//第一个骰子的选法
for(int i = 1, j = 1; i <= 6; i ++, j ++)
a[i][j] = 4;
//第一个已选
n -= 1;
while(n)
{
if(n & 1) mul(a, f);
mul(f, f);
n >>= 1;
}
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= 6; i ++)
for(int j = 1; j <= 6; j ++)
f[i][j] = 4;
//f[1][4] = f[4][1] = f[2][5] = f[5][2] = f[3][6] = f[6][3] = 0;
for(int i = 0; i < m; i ++)
{
int a, b;
cin >> a >> b;
//矩阵选了代表上一个的朝上的数后,下一个朝上的数可以选什么
//所以是一个映射一个不映射
f[a][r[b]] = 0;
f[b][r[a]] = 0;
}
qmi(n);
//mul(a, f);
for(int i = 1; i <= 6; i ++)
for(int j = 1; j <= 6; j ++)
{
//cout << a[i][j] << endl;
//加起来就是方案数
ans = (ans + a[i][j]) % mod;
}
cout << ans << endl;
return 0;
}