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 中国大陆许可协议进行许可。

posted @   Gold_stein  阅读(21)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
  1. 1 逃离地面 RAD & 三浦透子
逃离地面 - RAD & 三浦透子
00:00 / 00:00
An audio error has occurred.

作词 : 野田洋次郎

作曲 : 野田洋次郎

空飛ぶ羽根と引き換えに 繋ぎ合う手を選んだ僕ら

それでも空に魅せられて 夢を重ねるのは罪か

夏は秋の背中を見て その顔を思い浮かべる

憧れなのか、恋なのか 叶わぬと知っていながら

重力が眠りにつく 1000年に一度の今日

太陽の死角に立ち 僕らこの星を出よう

彼が眼を覚ました時 連れ戻せない場所へ

「せーの」で大地を蹴って ここではない星へ

行こう

もう少しで運命の向こう もう少しで文明の向こう

もう少しで運命の向こう もう少しで

夢に僕らで帆を張って 来たるべき日のために夜を超え

いざ期待だけ満タンで あとはどうにかなるさと 肩を組んだ

怖くないわけない でも止まんない

ピンチの先回りしたって 僕らじゃしょうがない

僕らの恋が言う 声が言う

「行け」と言う