問題描述
忙碌了一個學期的 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]表示第天爲止,穿衣服的快樂值,因此我們可以得到下面的狀態轉移方程:
其中,k是枚舉前一天穿的衣服。經過計算,我們可以發現時間複雜度達到了,所以必須做到優化,觀察這個狀態轉移方程的形式,聯想到矩陣乘法公式:
將矩陣中的看作,將矩陣中的看作。我們可以得到:
觀察公式與公式,與相對應,與相對應。想到或許可以進行矩陣快速冪優化,矩陣快速冪實現的條件是矩陣乘法具有結合律,而公式是具有結合律的。證明方法如下:
設A、B、C分別爲的矩陣
因此公式可以使用矩陣快速冪來進行優化,矩陣乘法的定義由公式更改爲公式。轉移矩陣如下:
初始狀態均爲0,最終結果從次冪的矩陣中找最大值即可。
需要注意的是,由於矩陣乘法定義改變,不要忘記在矩陣快速冪中的單位矩陣設置爲全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;
}