P8624 [蓝桥杯 2015 省 AB] 垒骰子
这道题的数据范围比较突出:
1<=N<=1e9
先写一个O(N)算法:
#include <iostream> #include <stdio.h> #include <algorithm> #include <cstring> #define int long long using namespace std; const int mod = 1e9 + 7; int n, m, g[8][8], f[8], op[8], bf[8]; signed main() { op[1] = 4; op[4] = 1; op[2] = 5; op[5] = 2; op[3] = 6; op[6] = 3; cin >> n >> m; for (int i = 1; i <= m; i++) { int x, y; cin >> x >> y; g[x][y] = g[y][x] = 1; } for(int i = 1; i <= 6; i++) f[i] = 4; for (int i = 2; i <= n; i++) { memcpy(bf, f, sizeof(f)); for (int j = 1; j <= 6; j++) { int op_j = op[j], tmp = 0; for(int k = 1; k <= 6; k++) { int p = (1 - g[op_j][k]) * 4; tmp = (tmp + p * bf[k]) % mod; f[j] = tmp; } } } int ans = 0; for(int i = 1; i <= 6; i++) ans = (ans + f[i]) % mod; cout << ans << endl; system("pause"); return 0; }
f[i][j]表示:高度为i层,且最顶上的骰子数字j朝上的方案数。
一开始受到ACwing中用spfa算法求右边数限制的最短路那道题的影响,陷入了定势思维,
把dp的转移方程写成了
for (int i = 2; i <= n; i++) { memcpy(bf, f, sizeof(f)); for (int j = 1; j <= 6; j++) { int op_j = op[j]; int &tmp = f[j]; for(int k = 1; k <= 6; k++) { int p = (1 - g[op_j][k]) * 4; tmp = (tmp + p * bf[k]) % mod; } } }
仔细观察就能发现,这样子会导致f[j]被多次累加更新,导致计算值远大于实际值。
(如果用的是二维数组这么写没有问题,但这道题因为省掉了一维数组,所以必须要借助一个临时变量)
正解:
注意正解和上面O(N)做法在思维方式上的不同:
O(N)做法考虑的是:我当前在第i层,我要怎么从第i-1层转移过来
正解矩阵乘法考虑的是:我当前在第i-1层,我要怎么转移到第i层
如果套用前一种思维模式的话,就会导致在给矩阵填系数的时候出错。
tip:线性代数都忘光啦!单位矩阵的形式:对角线全1,其他全0
正解代码:
#include <iostream> #include <stdio.h> #include <algorithm> #include <cstring> #define For(i, j, n) for (int i = j; i <= n; ++i) using namespace std; typedef long long ll; const int mod = 1e9 + 7; int n, m, op[7]; struct Matrix { int x, y; int **M; Matrix(int i, int j) { x = i; y = j; M = new int *[x + 1]; for (int i = 1; i <= x; i++) M[i] = new int[y + 1]; } Matrix *operator*(Matrix &t) { Matrix *tmp = new Matrix(6, 6); for (int i = 1; i <= x; i++) for (int j = 1; j <= t.y; j++) { int int_tmp = 0; for (int k = 1; k <= y; k++) int_tmp = ((ll)int_tmp + (ll)M[i][k] * (ll)t.M[k][j]) % mod; tmp->M[i][j] = int_tmp; } return tmp; } }; Matrix *quick_power(Matrix *a, int x) { Matrix *tmp = new Matrix(6, 6); for(int i = 1; i <= 6; i++) for(int j = 1; j <= 6; j++) if(i == j)tmp->M[i][j] = 1; else tmp->M[i][j] = 0; while (x) { if (x & 1) tmp = (*tmp) * (*a); a = (*a) * (*a); x >>= 1; } return tmp; } signed main() { op[1] = 4; op[4] = 1; op[2] = 5; op[5] = 2; op[3] = 6; op[6] = 3; cin >> n >> m; Matrix *st = new Matrix(1, 6), *mult = new Matrix(6, 6); For(i, 1, 6) For(j, 1, 6) mult->M[i][j] = 4; For(i, 1, 6) st->M[1][i] = 4; For(i, 1, m) { int a, b; cin >> a >> b; mult->M[a][op[b]] = 0; mult->M[b][op[a]] = 0; } st = (*st) * (*quick_power(mult, n - 1)); int ans = 0; for(int i = 1; i <= 6; i++) ans = (ans + st->M[1][i]) % mod; cout << ans << endl; return 0; }
注意:用new或者malloc分配的数组变量不会默认初始化为0,要手动初始化。
本文作者:smartljy
本文链接:https://www.cnblogs.com/smartljy/p/17880082.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步