poj 3734 Blocks (矩阵快速幂优化的动态规划)

题目大意:

有4种颜色,去涂满n个格子,其中要求红和绿两种颜色涂偶数次。问:最终有多少种不同的填涂方案。

解题思路:

一开始以为是排列组合,但是n的范围太大。

可以很明显的得到一个dp方程:dp[i][j][k] 表示涂第i 位,红色是否为偶数,绿色是否为偶数,值表示种数。

dp[i][0][0] = dp[i-1][0][0]*2+dp[i-1][0][1]*1+dp[i-1][1][0]*1+dp[i-1][1][1]*0,其他的同理。最后只要输出dp[n][0][0]就可以了。

一开始,我初始化 dp[1][0][0] = 2 出错了。然后想到 一个都不涂的时候,也算一种填涂方案。所以把dp初始化为dp[0][0][0] = 1,其他为0就对了。

如果我们按照n一位一位的推导,由于n很大,所以肯定不行。


我们可以把推导式 通过矩阵的方式进行存储推导,那么就可以用矩阵快速幂的方式进行矩阵优化。


矩阵快速幂的运算,跟一般的快速幂一样,也是运用了二分的思想。单位矩阵相当于一般快速幂的数字 1 

具体代码如下:

在下面的代码中,dp用两维表示,而不是之前的三维,其中第一维意义不变,第二维   0 表示 红奇 绿奇 ; 1 表示 红奇 绿偶 ; 2 表示 红偶 绿奇; 3 表示 红偶 绿偶。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
using namespace std;
struct node//矩阵
{
    int a[5][5];
    node()
    {
        memset(a,0,sizeof(a));
    }
};
void setNodeI(node &x)//设置为单位矩阵
{
    for(int i = 0; i < 5; i++)
    {
        x.a[i][i]=1;
    }
}
node mul(node x,node y,int n,int k,int m)//n行k列与k行m列的矩阵相乘
{
    node z;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<m; j++)
        {
            for(int t=0; t<k; t++)
            {
                z.a[i][j] += (x.a[i][t])*(y.a[t][j]);
            }
            z.a[i][j]%= 10007;
        }
    }
    return z;
}
node qmul(node x,int cnt)//矩阵快速幂
{
    node I;
    setNodeI(I);
    while(cnt>=1)
    {
        if(cnt%2) I = mul(I,x,4,4,4);
        x = mul(x,x,4,4,4);
        cnt = cnt>>1;
    }
    return I;
}
int main()
{
    node one;
    node tem;
    int n,m,i,j;
    one.a[3][0]=1;//初始化dp
    tem.a[0][0]=2;tem.a[0][1]=tem.a[0][2]=1;//初始化原始矩阵
    tem.a[1][1]=2;tem.a[1][0]=tem.a[1][3]=1;
    tem.a[2][2]=2;tem.a[2][0]=tem.a[2][3]=1;
    tem.a[3][3]=2;tem.a[3][1]=tem.a[3][2]=1;
    while(scanf("%d",&n)==1)
    {
        while(n--)
        {
            scanf("%d",&m);
            node tmp = tem;
            node ans = qmul(tmp,m);
            ans = mul(ans,one,4,4,1);
            printf("%d\n",ans.a[3][0]);
        }
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/u011561033/article/details/45868335