【BZOJ】1002 轮状病毒


【供参考】

VFleaking的题解

生成树计数


【分析】

很明显的生成树计数,用基尔霍夫矩阵求行列式可以解出来。但是如果使用递归定义求解,会TLE;如果使用高斯消元,会出现数太大导致无法运算。所以这是不行的。

然而我们还没有利用这个行列式的特殊性,还有计数问题通常从组合数学或者动态规划的角度入手,而且输入数据这么少,所以应该使用递推的方法。
我们设 f[i] 表示 i 阶轮状病毒的个数。

接下来,我们大胆地猜想它是可以线性递推的

先把几个小的解给求出来。
这里可以手算,也可以写一个暴力的程序,但是最好还是写一个暴力的程序,不然会很烦的,而且耗时间。

f(1)=1

f(2)=5

f(3)=16

f(4)=3101131001311013=3310131013+101131013+101310131=45

f(5)=3100113100013100013110013=121

f(6)=320

接下来,我们就要求解线性递推式,和上面一样,要么手算,要么打个解方程的代码
由于我们求了 f(1),f(2),f(3),f(4),f(5),f(6) ,所以我们最多可以设 4 个元,我们这里就只设3个元了。
f(n)=af(n1)+bf(n2)+c
①当 n=4 时, 16a+5b+c=45
②当 n=5 时, 45a+16b+c=121
③当 n=6 时, 121a+45b+c=320
由①②③得

16a+5b+c=4545a+16b+c=121121a+45b+c=320

即解矩阵
16451215164511145121320

解得 a=3,b=1,c=2
f(n)=3f(n1)f(n2)+2

问题解决,最后注意要高精度即可。

目前还欠了个账,就是直接用行列式求递推式的推导。数学基础暂时不够,以后再补。


【代码】

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

const int N=101;

struct Q
{
    int p[N];
}f[N],t;
int n;

inline Q plus(Q qa,Q qb)
{
    Q s; memset(s.p,0,sizeof s.p); s.p[0]=(qa.p[0]<qb.p[0]?qb.p[0]:qa.p[0]);
    int m,g=0;
    for (int i=1;i<=s.p[0];i++)
    {
        m=qa.p[i]+qb.p[i]+g;
        s.p[i]=m%10;
        g=m/10;
    }
    if (g) s.p[++s.p[0]]=1;
    return s;
}

inline Q minus(Q qa,Q qb)
{
    Q s; memset(s.p,0,sizeof s.p); s.p[0]=qa.p[0];
    int m,g=0;
    for (int i=1;i<=s.p[0];i++)
    {
        m=qa.p[i]-qb.p[i]-g;
        if (m<0) m+=10,g=1; else g=0;
        s.p[i]=m;
    }
    for (;!s.p[s.p[0]];s.p[0]--);
    return s;
}

int main(void)
{    
    scanf("%d",&n);

    f[1].p[0]=f[1].p[1]=1;
    f[2].p[0]=1,f[2].p[1]=5;
    t.p[0]=1,t.p[1]=2;
    for (int i=3;i<=n;i++) 
        f[i]=plus(minus(plus(f[i-1],plus(f[i-1],f[i-1])),f[i-2]),t);

    for (int i=f[n].p[0];i;i--) printf("%d",f[n].p[i]);
    printf("\n");

    return 0;
}

【小结】

这道题,给了我们一些很好的思路。

首先要明确一点:
当设计出一个算法,且想不到更好的注意时,要想想自己没有利用哪些题目的特殊性,从特殊性进行方法的调整。

然后,这道题是计数问题,下面总结的即一些计数问题的技巧方法。

计数问题,入手的角度通常有两个:组合数学,动态规划。当然还有一些特殊的模型,如本题生成树计数用基尔霍夫矩阵求行列式值。

对于从动态规划入手的问题,有一个常见的技巧。

首先算出小数据的答案,然后求解递推式。

②能这样干的特征通常是:输入数据少

③递推式通常我们猜想是线性的,有时可能也会涉及到 f(n)2 这种恶心的东西。

设元时不要遗漏常数项

设元个数 p
当前我们求了f[1],f[2],...,f[n],假如存在线性递推式,那么我们可以设多少个元呢?
首先能设 p 个元,则有:

f[i]=a1f[i1]+a2f[i2]+...+ap1f[p1]+ap

那么 f[1],f[2],...,f[p1] 都不可以用于方程, f[p],f[p+1],...,f[n] 都可以,即可以列 np+1 个方程。

要能求解,所以 pnp+1 pn+12

⑥求值的会特别的讨厌,在这时候有两种方法:
- 方法1:打个暴力的代码
- 方法2:手算
求解递推式的过程同样讨厌,一样的有以上两种方法。
然而像本题这样的都涉及到高斯消元的,打个程序的性价比就会比较高了。
反正——酌情而定吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值