POJ 3734 Blocks 动态规划(矩阵的幂)

一、题目大意

我们要给排成一行的区块涂颜色,可以选择红、绿、蓝、黄四种,要求红和绿的块都必须是偶数个,求出最终的涂色方式,对10007取余。

二、解题思路

我们设三个数列A,B和C:

1、A代表红色和绿色都为偶数的组合数。

2、B代表红色为奇数,绿色为偶数的组合数。

3、C代表红色和绿色都为奇数的组合数。

题目给出A[1]=2,A[2]=6,接下来我们推理A[3]和A[N]

当第一块不是红和绿时,有 A[2] * 2种,

当第一块是红色时,剩下的需要让红色为奇数个且绿色为偶数个,那么剩下的直接按照B[2]的排列方式即可。

当第一块为绿色时,剩下的需要让绿色为奇数个且红色为偶数个,这种情况在数值上和B[2]相等。

则 A[3] = A[2] * 2 + B[2] * 2

同时不难看出,A[2] = A[1] * 2 + B[1] * 2

所以 A[3] = A[1] * (2 ^ 2) + B[1] * (2 ^ 2) + B[2] * 2,代入A[1]=2

A[3] = 2 ^ 3 + B[1] * (2 ^ 2) + B[2] * 2

推出A[N] = 2 ^ N + B[1] * (2 ^ (N - 1)) + B[2] * (2 ^ (N - 2)) + ... B[N-1] * (2 ^ 1) = A[N-1] * 2 + B[N-1] * 2

接下来再来推C数列,不难看出C[1] = 0,C[2] = 2,接下来推 C[3] 和 C[N]

当第一块不为红色和绿色时,排列数为 2 * C[2]。

当第一块为绿色时,剩余的块需要绿色为偶数,红色为奇数,即B[2]种。

当第二块为红色时,剩余的块需要红色为偶数且绿色为奇数,这种情况数值上等于B[2]

则C[3] = C[2] * 2 + B[2] * 2

同理C[4] = C[3] * 2 + B[3] * 2

不难看出 C[2] 也等于 C[1] * 2 + B[1] * 2

我们把C[1] = 0代入C[2]个式子

C[2] = B[1] * 2

把 C[2] 待入 C[3] 的式子

C[3] = B[1] * (2 ^ 2) + B[2] * 2

把 C[3] 带入 C4 的式子

C[4] = B[1] * (2 ^ 3) + B[2] * (2 ^ 2) + B[3] * 2

则C[N] = B[1] * (2 ^ (N - 1)) + B[2] * (2 ^ (N  - 2)) + ... B[N-1] * (2 ^ 1) = A[N] - 2 ^ N

接下来我们来推理数列B,不难看出 B[1] = 1,B[2] = 2,接下来推理 B[3]和 B[N]

首先考虑第一块不为红色和绿色的情况,剩余块需要有奇数个红色和偶数个绿色,排列数为 B[2] * 2。

接下俩考虑第一块为红色的情况,剩余块需要有偶数个红色和偶数个绿色,则剩余块的排列数等于 A[2]。

接下来考虑第一块为绿色的情况,剩余块需要有奇数个红色和奇数个绿色,则剩余块的排列数等于C[2]。

那么B[3] = B[2] * 2 + A[2] + C[2]

则 B[N] = B[N-1] * 2 + A[N-1] + C[N-1]

同时C[N-1] = A[N-1] - 2 ^ N

则可推出两个式子

式子1:B[N] = B[N-1] * 2 + A[N-1] * 2 - 2 ^ N

式子2:B[N+1] = B[N] * 2 + A[N] * 2  - 2 ^ (N + 1)

把A[N] = A[N-1] * 2 + B[N-1] * 2 代入式子2

式子2:B[N+1] = B[N] * 2 + A[N-1] * 4 + B[N-1] * 4 - 2 ^(N + 1)

把式子1乘以2

式子1:2 * B[N] = B[N-1] * 4 + A[N-1] * 4 - 2 ^ (N + 1)

式子2减式子1得到式子3

式子3:B[N+1] - 2 * B[N] = 2 * B[N] 

则 B[N+1] = B[N] * 4

把 B[N + 1] = B[N] * 4,且B1 = 1,代入A的递推式

得出 A[N+1] = A[N] * 2 + 2 ^ (2 * N - 1)。

再把递推式写成矩阵的形式。

那么定义矩阵A为 2 1 // 0 4,然后A = A的 n - 1次幂

最终结果为(A[0][0] * 2 + A[0][1] * 2) % M

三、代码

#include <iostream>
#include <vector>
using namespace std;
typedef vector<int> vec;
typedef vector<vec> mat;
const int M = 10007;
mat mul(mat &A, mat &B)
{
    mat C = mat(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++)
            {
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % M;
            }
        }
    }
    return C;
}
mat pow(mat &A, int N)
{
    mat B = mat(A.size(), vec(A[0].size()));
    for (int i = 0; i < B.size(); i++)
    {
        B[i][i] = 1;
    }
    while (N > 0)
    {
        if (N & 1)
        {
            B = mul(B, A);
        }
        A = mul(A, A);
        N >>= 1;
    }
    return B;
}
void solve(int N)
{
    mat A = mat(2, vec(2));
    A[0][0] = 2;
    A[0][1] = 1;
    A[1][1] = 4;
    A = pow(A, N - 1);
    int res = (A[0][0] * 2 + A[0][1] * 2) % M;
    printf("%d\n", res);
}
int main()
{
    int T, N;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &N);
        solve(N);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值