hdu 2294 Pendant (动态规划+矩阵) 解题报告

20 篇文章 0 订阅
17 篇文章 0 订阅
 hdu 2294 Pendant  (动态规划+矩阵) 解题报告

原题传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2294

还是推荐一个我觉得不错的解题报告吧,虽然其中有几处错误,但是这个解题报告还是值得一看的

出处地址:http://blog.sina.com.cn/s/blog_5f5353cc0100hq81.html

本题从表面上看是排列组合题,但要推出公式还是有相当难度。所以想到用DP来做。方程可通过枚举几种情况来推出。

   以F[i][j]表示长度为i的pendant,用了j种珍珠,所构成的方案数,则F[i][j]=F[i-1][j]*j+F[i-1][j-1]*(k-j+1)。结果就是F[1][k]+…+F[n][k]。但注意到N的范围很大,申请那么大的数组会MLE。

   注意到当前状态只与上一个状态有关,那么可以使用循环数组来,并累加上每个F[I][K]的方法来做。但这样的复杂度为O(NK),会TLE。

   优化的方法是使用矩阵来做。将F[i-1]到F[i]的转移用矩阵来描述,相当于一个k*k的线性变换矩阵。因此F[i]=A*F[i-1],这里A是转移矩阵,即F[i]=Ai-1*F[1],所以F[1]+…+F[n]=A0*F[1]+…+An-1*F[1]=(E+A+A2+…+An-1)*F[1]。

   F[i-1]这个矩阵是

F[i-1][0]  F[i-1][1]  F[i-1][2] ......  F[i-1][k]

0        0        0      ……  0     

.         .        .       .     .

.         .        .       .     .

0        0        0      0     0

(这是个K+1阶的阶阵)

 

  A矩阵是

0          k  0    0  0  0

0  1  k-1  0  0  0

0  0  2    0  0  0

.   .  .    .   .   .

.   .  .    .   .   .

0  0  0   0   k-1  1

0  0  0   0   0   k

 

这两个矩阵为什么是这样的,用矩阵的乘法来乘一下,将结果与DP方程对比下就知道了。

接下来要解决的问题就是F[1]要怎样填写。每次再另写一个程序来填F[1]?大可不必。因为F[1]=F[0]*A,那么只要填F[0]就行了。将F[0]变为E(单位阵)就可以了。

最后的问题就是求矩阵的平方和了。这里的方法很多,到网上一找也很容易找到,就不多说了。下面用的是二分求和,二分求幂。


 

我觉得原文中的F[i] = A*F[i-1]应该是F[i] = F[i-1] * A;

通过这个我知道了矩阵的一些性质:在有意义的情况下,满足结合律和分配律,不满足交换律。好吧,我承认我的线性代数学的不好,数学知识对于算法来说太重要了,特别是acm这样的东西,我真后悔当初上课的时候睡觉了!!

F[i][j]=F[i-1][j]*j+F[i-1][j-1]*(k-j+1)状态方程很简单,但是这个题中的数据量太大,没法用O(n)的复杂度解决,需要优化!

用矩阵解决递推式我做过好几个了,最经典的就是斐波那契,大家都懂的f[i] = f[i-1] + f[i-2];

但是很迷糊,后来遇到一个难得f[i] = a*f[i-1]+b*f[i-2];

这个题的矩阵确实不简单,我推了好久,不过好在有模板,推出来之后就直接套模板了,

下面是源码,可以借鉴,这个模板很经典!比网上搜到的其他的解题报告中的代码都好!呵呵,可能是我搜到的都不怎么样吧!模板来自我同学zys,最早的主人佚名

模板的作用是求A + A^2 + A ^3 + A^4 ......A^k 其中A是矩阵 下面代码中有一个先加E在减E的过程,这个不知对不对,看官自己细细的体会吧!

!!下面的代码我用G++提交没问题,但是用c++提交就爆栈,不明原因,求解释

#include<iostream>
#define CLR(NAME,VALUE) memset(NAME,VALUE,sizeof(NAME))
void MIN(int& a,int b) { if( a>b )  a=b; }
void MAX(int& a,int b) { if( a<b )  a=b; }

using namespace std;

int m=1234567891;
int k,n;
struct node
{
    long long matrix[31][62];
};
node multiply(node a,node b)
{
    node res;
    int i,j,k;
    memset(res.matrix,0,sizeof(res.matrix));
    for(i=0;i<n;++i)
        for(j=0;j<n;++j)
        {
            for(k=0;k<n;++k)        //不管是初始化还是加法还是乘法都要记得取余
                res.matrix[i][j]=(res.matrix[i][j] + a.matrix[i][k] * b.matrix[k][j])%m;
        }
        for(i=0;i<n;++i)
            for(j=n;j<2*n;++j)
            {
                res.matrix[i][j]=a.matrix[i][j]%m;
                for(k=0;k<n;++k)        //如果是到最后才取余,则会wa
                    res.matrix[i][j]=(res.matrix[i][j] + a.matrix[i][k] * b.matrix[k][j])%m;
            }
    return res;
}
node pow(node mtx,int k)
{
    if(k==1)
        return mtx;
    else if(k%2)
        return multiply( pow(multiply(mtx,mtx),k/2) , mtx );
    else
        return pow(multiply(mtx,mtx),k/2);
}
node series;

int main() {
       int ca,i,j;

       scanf("%d",&ca);
       while( ca-- ) {
            scanf("%d%d",&k,&n);
            //init
            n++;
            for(i = 0;i < n;i++)
                for(j = 0;j < n*2;j++)
                    series.matrix[i][j] = 0;
            //将B矩阵变为单位阵
            //series.matrix[0][0] = 0;
            for(i=1;i<n;++i)
            {
                series.matrix[i-1][i]=n-i;
                series.matrix[i][i]=i;
            }

            for(i=0,j=n;i<n;++i,++j)        //如果i==j那么矩阵中此值就是1,否则为0,就是主对角线是1的单位矩阵
                series.matrix[i][j]=1;

            series=pow(series,k+1);

            for(i=0,j=n;i<n;++i,++j)
                series.matrix[i][j]--;

            printf("%d\n",series.matrix[0][2*n-1]);
       }

     return 0;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值