zoukankan      html  css  js  c++  java
  • C++矩阵加速经典题目:Warcraft III 守望者的烦恼 [vijos 1067]

    Warcraft III 守望者的烦恼

    背景
    守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。

    描述
    头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。

    守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?

    格式
    输入格式
    第一行是闪烁技能的等级k(1<=k<=10)
    第二行是监狱的个数n(1<=n<=2^31-1)

    输出格式
    由于方案个数会很多,所以输出它 mod 7777777后的结果就行了

    样例1
    样例输入1
    2
    4
    样例输出1
    5
    限制
    各个测试点1s

    提示
    把监狱编号1 2 3 4,闪烁技能为2级,
    一共有5种方案
    →1→2→3→4
    →2→3→4
    →2→4
    →1→3→4
    →1→2→4

    小提示:建议用int64,否则可能会溢出

    这道题我们可以先考虑k=2的情况,发现其实此题的主要内容就是爬楼梯(k为最多爬多少步,n为有n阶楼梯)。所以在k=2时整个解排出来便是一个裴波那契数列,递推式为(f_{n}=f_{n-1}+f_{n-2})。那么扩展到闪烁步数最大为k的情况,不难发现递推式便为(f_{n}=f_{n-1}+f_{n-2}+f_{n-3}+...+f_{n-k})

    这里有一个初始化的问题让我思考了一段时间,这里阐述一下。如果k是小于n的,那么总的方案数会是多少。不难发现在(k>n)时,你最多一次只能爬n步,而不能爬k步,所以在(k>n)时,其实方案数就是(f_{n}),也就是(f_{n}=f_{n-1}+f_{n-2}+...+f_{0})

    现在来考虑矩阵加速,方法类似于裴波那契数列的构造方法,具体请看此篇博客:https://blog.csdn.net/weixin_44049566/article/details/88946702
    这里因为矩阵的阶数随k而定,所以可以使用循环来构造,类似于如下代码:

    void pre1(int k) {
            n=m=k;
            for(int i=1;i<=k-1;i++) c[i+1][i]=1;
            for(int i=1;i<=k;i++) c[i][k]=1;
        }    
    

    加速矩阵构造出来类似于这样:
    (egin{bmatrix} & 0 & 0 & ... &0 \ & 1 & 0& ... & 0\ & 0 & 1& ... & 0\ & ... & ... &... & ...\ & 0 & 0& ... &1 end{bmatrix})*(egin{bmatrix} & f_{1}&f_{2}&...&f_{k}end{bmatrix})

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
       
    #define N 20
    #define LL long long
    #define MOD 7777777
     
    LL k,n;
     
    struct Matrix {
        LL n,m,c[N][N];
        Matrix() { memset(c,0,sizeof(c)); };
        void _read() {
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    scanf("%lld",&c[i][j]);
        }
        Matrix operator * (const Matrix& a) {
            Matrix r;
            r.n=n;r.m=a.m;
            for(int i=1;i<=r.n;i++)
                for(int j=1;j<=r.m;j++)
                    for(int k=1;k<=m;k++)
                        r.c[i][j]= (r.c[i][j]+ (c[i][k] * a.c[k][j])%MOD)%MOD;
            return r;
        }
        void _print() {
            for(int i=1;i<=n;i++) {
                for(int j=1;j<=m;j++) {
                    if(j!=1) cout<<" ";
                    cout<<c[i][j];
                }
                if(i!=n) puts("");
            }
        }
        void pre1(int k) {
            n=m=k;
            for(int i=1;i<=k-1;i++) c[i+1][i]=1;
            for(int i=1;i<=k;i++) c[i][k]=1;
        }    
        void pre2(int k) {
            n=1,m=k;
            c[1][0]=c[1][1]=1;
            for(int i=2;i<=k;i++)
                for(int j=i-1;j>=0;j--)
                    c[1][i]+=c[1][j];
        }
        Matrix _power(int indexx) {
            Matrix tmp,sum;tmp.pre1(k);
            for(int i=1;i<=k;i++) sum.c[i][i]=1;sum.m=sum.n=k;
            while(indexx>0) {
                if(indexx&1) sum=sum*tmp;
                tmp=tmp*tmp;
                indexx/=2;
            }
            return sum;
        }
         
    }A,B;
     
    int main() {
        cin>>k>>n;
        A.pre2(k);
        B.pre1(k);
        B=B._power(n-k);
        A=A*B;
        cout<<A.c[1][k];
    }
    
  • 相关阅读:
    【题解】Luogu p2016 战略游戏 (最小点覆盖)
    【模板】Linux下输出文件的对比
    【题解】Luogu P3052 【USACO12】摩天大楼里的奶牛Cows in a Skyscraper
    【题解】滑雪 luogu1434 记忆化搜索
    【题解】Luogu P3110 [USACO14DEC]驮运Piggy Back
    【题解】Luogu P3123 [USACO15OPEN]贝茜说哞Bessie Goes Moo
    【题解】Luogu P2214 [USACO14MAR]哞哞哞Mooo Moo
    【题解】Luogu P2327 [SCOI2005]扫雷
    【题解】Luogu P1011 车站
    【题解】Luogu P2889 [USACO07NOV]挤奶的时间Milking Time
  • 原文地址:https://www.cnblogs.com/MisakaMKT/p/10689051.html
Copyright © 2011-2022 走看看