zoukankan      html  css  js  c++  java
  • 【经典】矩阵快速幂加速dp递推——namomo #1 c

    有幸打上了wls的举办的第一场比赛

    知道一定是矩阵快速幂加速dp,但是递推公式一下子有点难弄

    看了题解后才发现可以将这些六边形平放,平放之后很容易发现从第i个到第i+1的关键点是1,2:

      因为第i+1个六边形所有点的所有方向的状态,都是由第i个六边形1或2的方向推出的

    所以我们只要dp第i个六边形点1,2两个向右的方向即可

    dp[i,0]表示起点到第i个六边形1号点方向右上的最小值
    dp[i,1]:第i个六边形1号点方向右的最小值
    dp[i,2]:第i个六边形2号点方向右上的最小值
    dp[i,3]:第i个六边形2号点方向右的最小值
    那么从dp[i]->dp[i+1]就会有一个固定的递推方案
    那么就构造一个4*4的转移矩阵 
        |2 1 2 3|
    A=  |1 2 1 0|
        |0 1 2 2|
        |2 2 1 2|
    矩阵快速幂 
    [ dp[1,0],dp[1,1],dp[1,2],dp[1,3] ]*A^(n-1)=[ dp[n,0],dp[n,1],dp[n,2],dp[n,3] ]     

    特殊情况:当x==k时会有特殊情况,x==k-1,且l=4或5时会有特殊情况,是dp推不出来的,此时特判下就好

    此外,本题的矩阵快速幂比较特殊,要将原来的乘法变成加法,原来的加法变成min操作(考虑下为什么)

    ps:写这种矩阵加速dp的题比较少,花了好久才弄对。。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define N 5
    
    struct M{
        ll a[N][N];
        M(){memset(a,0x3f,sizeof a);}
    }A;
    ll F[N];
    void mul(M A,ll F[]){//F*A
        ll res[N];
        memset(res,0x3f,sizeof res);
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                res[i]=min(res[i],F[j]+A.a[j][i]);
        memcpy(F,res,sizeof res);
    }
    void mulself(M & A,M B){
        M C;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
                for(int k=0;k<4;k++)
                    C.a[i][j]=min(C.a[i][j],A.a[i][k]+B.a[k][j]);
        memcpy(A.a,C.a,sizeof C.a);
    }
    
    
    ll x,y,k,l;
    
    int main(){
        int t;cin>>t;
        while(t--){
            memset(A.a,0x3f,sizeof A.a);
            cin>>x>>y>>k>>l;
            if(x>k)swap(x,k),swap(y,l);
            if(x==k){//特殊情况 
                if(abs(y-l)==2 || abs(y-l)==4)cout<<1<<'
    ';
                else cout<<0<<'
    ';
                continue;
            }else if(k==x+1 && (l==5||l==4)){
                if(l==5)l=1;
                else l=2;
                if(abs(y-l)==2 || abs(y-l)==4)cout<<1<<'
    ';
                else cout<<0<<'
    ';
                continue;
            } 
            if(y==1)     {F[0]=0;F[1]=0;F[2]=1;F[3]=1;}
            else if(y==2){F[0]=1;F[1]=1;F[2]=0;F[3]=0;}
            else if(y==3){F[0]=1;F[1]=1;F[2]=0;F[3]=1;}
            else if(y==4){F[0]=0;F[1]=1;F[2]=1;F[3]=1;}
            else if(y==5){F[0]=1;F[1]=1;F[2]=1;F[3]=0;}
            else if(y==6){F[0]=1;F[1]=0;F[2]=1;F[3]=1;}
            A.a[0][0]=2;A.a[0][1]=1;A.a[0][2]=2;A.a[0][3]=3;
            A.a[1][0]=1;A.a[1][1]=2;A.a[1][2]=1;A.a[1][3]=0;
            A.a[2][0]=0;A.a[2][1]=1;A.a[2][2]=2;A.a[2][3]=2;
            A.a[3][0]=2;A.a[3][1]=2;A.a[3][2]=1;A.a[3][3]=2;
            //F*A^(n-1)
            ll n=k-x-1;
            while(n){
                if(n%2)mul(A,F);
                n>>=1;mulself(A,A);
            }
            ll ans=0x3f3f3f3f3f3f3f3f;
            if(l==5){
                ans=min(min(F[0],F[1]),min(F[2],F[3])+1);
            }
            else if(l==4){
                ans=min(min(F[0],F[1])+1,min(F[2],F[3]));
            }
            else if(l==1){
                ans=min(min(F[0],F[1])+1,min(F[2],F[3]+2));
            }
            else if(l==2){
                ans=min(min(F[0]+2,F[1]),min(F[2],F[3])+1);
            }
            else if(l==3){
                ans=min(min(F[0],F[1])+1,min(F[2]+2,F[3]));
            }
            else if(l==6){
                ans=min(min(F[0],F[1]+2),min(F[2],F[3])+1);
            }
            cout<<ans<<'
    ';
        }    
    } 
  • 相关阅读:
    Python入门-函数进阶
    Python入门-初始函数
    Leetcode300. Longest Increasing Subsequence最长上升子序列
    Leetcode139. Word Break单词拆分
    Leetcode279. Perfect Squares完全平方数
    Leetcode319. Bulb Switcher灯泡开关
    Leetcode322. Coin Change零钱兑换
    二叉树三种遍历两种方法(递归和迭代)
    Leetcode145. Binary Tree Postorder Traversal二叉树的后序遍历
    Leetcode515. Find Largest Value in Each Tree Row在每个树行中找最大值
  • 原文地址:https://www.cnblogs.com/zsben991126/p/13149223.html
Copyright © 2011-2022 走看看