• 《算法竞赛进阶指南》0x34 矩阵乘法 石头游戏


    题目链接:https://www.acwing.com/problem/content/208/

    将矩阵变成一个向量,由于每秒变化一次,所以可以通过一个投影矩阵来投影到该空间中的另一个向量中去,当前向量在i位置对之后的向量的j位置有贡献的时候就需要对这个矩阵的(i,j)位置设置参数,将当前向量的第i个元素的k倍加到下一个向量的第j个位置。

    本题中由于操作数最长的长度是6,所以对于时间k,k+60和k的操作一定是相同的,故可以将操作时间t分解成q*60+r,60次一迭代,随后将1-r秒的变换矩阵再乘上去即可,最终的向量就是

    final=Aq*60+r*start,start中的第0个位置是1,而且一直是1不变,表示变换过程中的常数项,状态向量的维数都是n*m+1。

    这次学到了使用STL风格设置矩阵,而且向量、矩阵之间的乘法只要维数对应就可以通过一个函数进行相乘。使用resize函数对矩阵的维数进行改变。简化了代码。

    代码如下:

    #include<iostream>
    #include<vector>
    #include<string.h>
    #include<cstdio>
    using namespace std;
    typedef  long long ll;
    typedef vector<ll> vec;
    typedef vector<vec> mat;
    const int maxn = 12;
    int n,m,act,t;
    int a[maxn][maxn];
    char op[maxn][maxn];
    mat A,B[65],C;
    int c[maxn][maxn];//保存当前网格中使用到了哪一个操作数 
    int num(int i,int j){return (i-1)*m+j;}
    
    mat mul(mat& A,mat& B){//两个张量相乘 
        mat ans(A.size(),vec(B[0].size()));
        for(int i=0;i<A.size();i++)
            for(int j=0;j<B[0].size();j++)
                for(int k=0;k<B.size();k++){
                    ans[i][j]+=A[i][k]*B[k][j];
                }
        return ans;
    }
    int main(){
        cin>>n>>m>>t>>act;
        char s[100];
        for(int i=1;i<=n;i++){//每个格子选择的操作序列 
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)a[i][j]=s[j]-'0';
        }
        
        int p=n*m+1;//向量的长度 
        
        for(int i=0;i<act;i++)scanf("%s",op[i]);//操作序列 
        
        for(int i=1;i<=60;i++)B[i].resize(p,vec(p));
        
        for(int k=1;k<=60;k++){
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++){
                    int x=a[i][j],y=c[i][j];
                    if(op[x][y]>='0' && op[x][y]<='9'){
                        B[k][0][num(i,j)]=op[x][y]-'0';//0位置对num(i,j)位置的贡献为k 
                        B[k][num(i,j)][num(i,j)]=1;//自身位置的保持不变 
                    }
                    else if(op[x][y]=='N' && i>1)B[k][num(i,j)][num(i-1,j)]=1;
                    else if(op[x][y]=='S' && i<n)B[k][num(i,j)][num(i+1,j)]=1;
                    else if(op[x][y]=='W' && j>1)B[k][num(i,j)][num(i,j-1)]=1;
                    else if(op[x][y]=='E' && j<m)B[k][num(i,j)][num(i,j+1)]=1;
                    c[i][j]=(y+1)%(strlen(op[x]));
                }
            B[k][0][0]=1;//保证每次状态向量的第一个位置都是1 
        }
        
        C=B[1];
        for(int i=2;i<=60;i++)C=mul(C,B[i]);
        A.resize(1,vec(p));//通过resize对张量的长度进行重新赋值 
        A[0][0]=1;
        ll ans=0,w=t/60;
        while(w){
            if(w&1)A=mul(A,C);
            C=mul(C,C);
            w>>=1;
        }
        w=t%60;
        for(int i=1;i<=w;i++){
            A=mul(A,B[i]);
        }
        for(int i=0;i<p;i++)ans=max(ans,A[0][i]);
        cout<<ans<<endl;
        return 0;
    } 
  • 相关阅读:
    Libgdx之Music Sound 音效
    [Android]Activity的生命周期
    Android开发中无处不在的设计模式——动态代理模式
    Wireshark数据抓包分析——网络协议篇
    iOS-一个弹出菜单动画视图开源项目分享
    给Java开发人员的Play Framework(2.4)介绍 Part1:Play的优缺点以及适用场景
    mybatis自己学习的一些总结
    Cocos2d-x 源代码分析 : Scheduler(定时器) 源代码分析
    exe4j打包java应用程序
    删除sql server用户时报15138错误
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13259299.html
Copyright © 2020-2023  润新知