Q老師度假(變形矩陣快速冪優化DP)

問題描述

忙碌了一個學期的 Q老師 決定獎勵自己 N 天假期。

假期中不同的穿衣方式會有不同的快樂值。

已知 Q老師 一共有 M 件襯衫,且如果昨天穿的是襯衫 A,今天穿的是襯衫 B,則 Q老師 今天可以獲得 f[A][B] 快樂值。

在 N 天假期結束後,Q老師 最多可以獲得多少快樂值?

Input

輸入文件包含多組測試樣例,每組測試樣例格式描述如下:

第一行給出兩個整數 N M,分別代表假期長度與 Q老師 的襯衫總數。(2 ≤ N ≤ 100000, 1 ≤ M ≤ 100)

接下來 M 行,每行給出 M 個整數,其中第 i 行的第 j 個整數,表示 f[i][j]。(1 ≤ f[i][j] ≤ 1000000)

測試樣例組數不會超過 10。

Output

每組測試樣例輸出一行,表示 Q老師 可以獲得的最大快樂值。

Sample input

3 2
0 1
1 0
4 3
1 2 3
1 2 3
1 2 3

Sample output

2
9

解題思路

由於天數是連續的,明顯具有子結構的性質,所以這個題我們可以用DP來做。令f[i][j]表示第ii天爲止,穿jj衣服的快樂值,因此我們可以得到下面的狀態轉移方程:
f[i][j]=max(f[i1][k]+H[k][j]),1kM(1) f[i][j]=max(f[i-1][k]+H[k][j]), 1\le k\le M \tag{1}
其中,k是枚舉前一天穿的衣服。經過計算,我們可以發現時間複雜度達到了O(109)O(10^9),所以必須做到優化,觀察這個狀態轉移方程的形式,聯想到矩陣乘法公式:
f[i][j]=k=1M(f2[i][k]×f3[k][j])(2) f[i][j]=\sum_{k=1}^{M}(f_2[i][k]\times f_3[k][j]) \tag{2}
f2f_2矩陣中的f2[i][k]f_2[i][k]看作f[i1][k]f[i-1][k],將f3f_3矩陣中的f3[k][j]f_3[k][j]看作H[k][j]H[k][j]。我們可以得到:
f[i][j]=k=1M(f[i1][k]×H[k][j])(3) f[i][j]=\sum_{k=1}^{M}(f[i-1][k]\times H[k][j])\tag{3}
觀察公式(1)(1)與公式(3)(3)maxmax\sum相對應,++×\times相對應。想到或許可以進行矩陣快速冪優化,矩陣快速冪實現的條件是矩陣乘法具有結合律,而公式(1)(1)是具有結合律的。證明方法如下:

設A、B、C分別爲a×b,b×c,c×da\times b, b\times c, c\times d的矩陣
((AB)C)[i,j]=maxl=1c((AB)[i,l]+C[l,j])=maxl=1c(maxk=1b(A[i,k]+B[k,l])+C[l,j])=maxl=1cmaxk=1b(A[i,k]+B[k,l]+C[l,j])=maxk=1bmaxl=1c(A[i,k]+B[k,l]+C[l,j])=maxk=1b(A[i,k]+maxl=1c(B[k,l]+C[l,j]))=maxk=1b(A[i,k]+BC[k,j])=(A(BC))[i,j] \begin{aligned} ((AB)C)[i,j] &=\max_{l=1}^{c}((AB)[i,l]+C[l,j])\\ &=\max_{l=1}^{c}(\max_{k=1}^{b}(A[i,k]+B[k,l])+C[l,j])\\ &=\max_{l=1}^{c}\max_{k=1}^{b}(A[i,k]+B[k,l]+C[l,j])\\ &=\max_{k=1}^{b}\max_{l=1}^{c}(A[i,k]+B[k,l]+C[l,j])\\ &=\max_{k=1}^{b}(A[i,k]+\max_{l=1}^{c}(B[k,l]+C[l,j]))\\ &=\max_{k=1}^{b}(A[i,k]+BC[k,j])\\ &=(A(BC))[i,j] \end{aligned}
因此公式(1)(1)可以使用矩陣快速冪來進行優化,矩陣乘法的定義由公式(3)(3)更改爲公式(1)(1)。轉移矩陣如下:
ans[i]=[f[i][1]f[i][M]]=[H[1][1]H[M][1]H[1][M]H[M][M]][f[i1][1]f[i1][M]] ans[i]=\begin{bmatrix} f[i][1]\\ \vdots\\ f[i][M] \end{bmatrix}= \begin{bmatrix} H[1][1]&\cdots&H[M][1]\\ \vdots&\ddots&\vdots\\ H[1][M]&\cdots&H[M][M] \end{bmatrix}* \begin{bmatrix} f[i-1][1]\\ \vdots\\ f[i-1][M] \end{bmatrix}
初始狀態f[1][1]f[1][M]f[1][1]\sim f[1][M]均爲0,最終結果從n1n-1次冪的矩陣中找最大值即可。

需要注意的是,由於矩陣乘法定義改變,不要忘記在矩陣快速冪中的單位矩陣設置爲全0矩陣。

完整代碼

//#pragma GCC optimize(2)
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

struct Matrix{
    Matrix(int _size=0) {
        Size=_size; mat=new long long*[Size];
        for (int i=0; i<Size; i++) mat[i]=new long long[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=0;
    }
    Matrix(const Matrix& t){
        Size=t.Size; mat=new long long*[Size];
        for (int i=0; i<Size; i++) mat[i]=new long long[Size];
        memcpy(mat,t.mat,sizeof(mat));
    }
    ~Matrix(){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
    }
    Matrix operator*(const Matrix& t) const{//僞.乘法
        Matrix ret(Size);
        for (int i=0; i<Size; ++i)
            for (int j=0; j<Size; ++j)
                for (int k=0; k<Size; ++k)
                    ret.mat[i][j]=max(ret.mat[i][j],mat[i][k]+t.mat[k][j]);
        return ret;
    }
    Matrix operator%(const int m) const {
        Matrix ret(Size);
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                ret.mat[i][j]=mat[i][j]%m;
        return ret;
    }
    Matrix& operator=(const Matrix& t){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
        Size=t.Size;
        mat=new long long*[Size];
        for (int i=0; i<Size; i++) mat[i]=new long long[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=t.mat[i][j];
        return *this;
    }
    void quick_pow(int x){
        Matrix ret(Size);
        //for (int i=0; i<Size; i++) ret.mat[i][i]=1;//注意,乘法含義改變,單位矩陣全0
        while(x){
            if(x&1) {
                ret=ret*(*this);
            }
            *this=(*this)*(*this);
            x>>=1;
        }
        *this=ret;
    }
    void output(){
        printf("Size: %d\n",Size);
        for (int i=0; i<Size; i++){
            for (int j=0; j<Size; j++){
                printf("%lld ",mat[i][j]);
            }
            printf("\n");
        }
    }
    int Size;
    long long** mat;
};
int getint(){
    int x=0,s=1; char ch=' ';
    while(ch<'0' || ch>'9'){ ch=getchar(); if(ch=='-') s=-1;}
    while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
    return x*s;
}
int n,m;
int main(){
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    while(scanf("%d %d",&n,&m)!=EOF){
        Matrix mat1(m);
        for (int i=0; i<m; i++)
            for (int j=0; j<m; j++)
                scanf("%lld",&mat1.mat[i][j]);

        mat1.quick_pow(n-1);
        long long ans=0;
        for (int i=0; i<m; i++)
            for (int j=0; j<m; j++)
                ans=max(ans,mat1.mat[i][j]);
        //mat1.output();
        printf("%lld\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章