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

来源:互联网 发布:php是做什么的 编辑:程序博客网 时间:2024/05/29 17:52
 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  00  1  k-1  0  0  00  0  2    0  0  0.   .  .    .   .   ..   .  .    .   .   .0  0  0   0   k-1  10  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;}