文章目录
A - Q老师与石头剪刀布
题目大意
Q老师和Monika玩石头剪刀布,一共n局,Q老师知道对方每一局的出招,但是他只能出a次石头,b次剪刀,c次布,问他是否能赢过Monika(赢n/2(上取整) 次)。
输入
第一行一个整数 t(1 ≤ t ≤ 100)表示测试数据组数。然后接下来的 t 组数据,每一组都有三个整数:
- 第一行一个整数 n(1 ≤ n ≤ 100)
- 第二行包含三个整数 a, b, c(0 ≤ a, b, c ≤ n)。保证 a+b+c=n
- 第三行包含一个长度为 n 的字符串 s,字符串 s 由且仅由 ‘R’, ‘P’, ‘S’ 这三个字母组成。第 i 个字母 s[i] 表示 Monika 在第 i 轮的出招。字母 ‘R’ 表示石头,字母 ‘P’ 表示布,字母 ‘S’ 表示剪刀
输出
对于每组数据:
- 如果 Q老师 不能赢,则在第一行输出 “NO”(不含引号)
- 否则在第一行输出 “YES”(不含引号),在第二行输出 Q老师 的出招序列 t。要求 t 的长度为 n 且仅由 ‘R’, ‘P’, ‘S’ 这三个字母构成。t 中需要正好包含 a 个 ‘R’,b 个 ‘P’ 和 c 个 ‘S’
“YES”/"NO"是大小写不敏感的,但是 ‘R’, ‘P’, ‘S’ 是大小写敏感的。
基本思路
遍历Monika每一轮的出招,统计Q老师哪几轮可以赢并维护’R’,‘P’,‘S’剩余次数以及Q老师每轮的出招,0254可以使用一个长度为n的字符串s1,第i个字符表示第i轮Q老师出招,最开始可以全部初始化为’*’,表示该轮出招还不确定或该轮不可能获胜。
根据Q老师赢的轮数判断最终能不能赢,如果不能,输出"NO";
如果能,输出"YES",然后输出遍历s1,如果遇到’R’,‘P’,‘S’直接输出;如果遇到’*’,就从还可以出的招里面选一个(对应招数量减1)。
完整代码
#include<iostream>
#include<string>
char win[200];
using namespace std;
string s;
string s1;
int cnt[200];
const char cl[] = {'P', 'S', 'R'};
int wcnt;
int n;
int main ()
{
win['R'] = 'P';
win['P'] = 'S';
win['S'] = 'R';
int t;
cin>>t;
for(int k=0;k<t;k++)
{
cin>>n;
cin>>cnt['R']>>cnt['P']>>cnt['S'];
cin>>s;
s1="";
for(int i=0;i<n;i++) s1.push_back('*');
wcnt=0;
for(int i=0;i<n;i++)
{
if(cnt[win[s[i]]] > 0)
{
s1[i] = win[s[i]];
cnt[win[s[i]]]--;
wcnt++;
}
}
int st=n/2;
if(n % 2 == 1) st++;
if(wcnt < st) cout<<"NO"<<endl;
else
{
cout<<"YES"<<endl;
for(int i=0;i<n;i++)
{
if(s1[i] != '*') cout<<s1[i];
else
{
for(int j=0;j<3;j++)
{
if(cnt[cl[j]] > 0)
{
cout<<cl[j];
cnt[cl[j]]--;
break;
}
}
}
}
cout<<endl;
}
}
return 0;
}
B - Q老师与十字叉
题目大意
有一个n x m的字符方阵,仅由’.‘和’‘构成,现在可以将一些’.‘改成’*’,问最少需要修改多少’.‘才能使方阵中出现’‘组成的十字(存在一行一列全为’*’)。
输入
第一行包含一个整数 q (1 ≤ q ≤ 5 * 10^4) — 表示测试组数
对于每组数据:
- 第一行有两个整数 n 和 m (1 ≤ n, m ≤ 5 * 10^4, n * m ≤ 4 * 10^5) — 表示网格图的行数和列数
- 接下来的 n 行中每一行包含 m 个字符
- 保证 q 组数据中 n 的总和不超过 5 * 10^4, n*m 的总和不超过 4 * 10^5
输出
答案输出 q 行, 第 i 行包含一个整数 — 表示第 i 组数据的答案。
基本思路
这个题的数据规模给的比较特殊,对时间复杂度要求不是很高,但是对空间复杂度要求较高。
我的主要思路如下:
- 使用
map<pair<int, int>, char> mp
来存储方阵,而不是直接使用二维数组。 - 使用r[n]和c[m]分别存储每一行和每一列中包含的’.'的个数
- 在读入方阵时,即可完成mp和r[n],c[m]的维护
- 要想使第i行第j列形成十字,所需要修改的字符数为
r[i]+c[j]-(mp[pair<int, int>(i,j)] == '.')
(如果要修改交叉位置,只需修改1次)。
完整代码
#include<iostream>
#include<map>
using namespace std;
int q;
int n, m;
int r[50005];
int c[50005];
int score[500];
int res=0;
map<pair<int, int>, char > mp;
int main ()
{
score['*']=0;
score['.']=1;
cin>>q;
for(int k=0;k<q;k++)
{
cin>>n>>m;
for(int i=0;i<n;i++) r[i]=0;
for(int i=0;i<m;i++) c[i]=0;
mp.clear();
char tmp;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>tmp;
r[i] += score[tmp];
c[j] += score[tmp];
mp.insert({pair<int, int>(i, j), tmp});
}
}
res=n+m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
int cres = r[i] + c[j];
if(mp[pair<int, int>(i,j)] == '.') cres--;
res = min(res, cres);
}
}
cout<<res<<endl;
}
return 0;
}
C - Q老师的考验
题目大意
数列 f(x) 定义如下:
- 当 x < 10 时,f(x) = x;
- 当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。
输入
输人文件包含多组测试用例,每组测试用例格式如下:
第一行给定两个正整数 k m。(k < 2e9, m < 1e5)
第二行给定十个整数,分别表示 a0~a9。
输出
对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。
基本思路
难点在于k取值范围太大,暴力从前往后推搞不定。为此我们可以采取一种很巧妙的方法——矩阵快速幂,这种方法经常可以用于降低线性递推的复杂度。
首先要构造矩阵的递推式,从而找到常数矩阵。矩阵递推式如下:
[
f
(
n
)
f
(
n
−
1
)
f
(
n
−
2
)
f
(
n
−
3
)
f
(
n
−
4
)
f
(
n
−
5
)
f
(
n
−
6
)
f
(
n
−
7
)
f
(
n
−
8
)
f
(
n
−
9
)
]
=
[
a
0
a
1
a
2
a
3
a
4
a
5
a
6
a
7
a
8
a
9
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
]
⋅
[
f
(
n
−
1
)
f
(
n
−
2
)
f
(
n
−
3
)
f
(
n
−
4
)
f
(
n
−
5
)
f
(
n
−
6
)
f
(
n
−
7
)
f
(
n
−
8
)
f
(
n
−
9
)
f
(
n
−
10
)
]
\begin{bmatrix} f(n) \\ f(n-1) \\ f(n-2) \\ f(n-3) \\ f(n-4) \\ f(n-5) \\ f(n-6) \\ f(n-7) \\ f(n-8) \\ f(n-9) \end{bmatrix} \quad = \begin{bmatrix} a0 & a1 & a2 & a3 & a4 & a5 & a6 & a7 & a8 & a9 \\ 1&0&0&0&0&0&0&0&0&0\\ 0 & 1 & 0&0&0&0&0&0&0&0 \\ 0&0&1&0&0&0&0&0&0&0\\ 0&0&0&1&0&0&0&0&0&0\\ 0&0&0&0&1&0&0&0&0&0\\ 0&0&0&0&0&1&0&0&0&0\\ 0&0&0&0&0&0&1&0&0&0\\ 0&0&0&0&0&0&0&1&0&0\\ 0&0&0&0&0&0&0&0&1&0\\ \end{bmatrix} \cdot \begin{bmatrix} f(n-1) \\ f(n-2) \\ f(n-3) \\ f(n-4) \\ f(n-5) \\ f(n-6) \\ f(n-7) \\ f(n-8) \\ f(n-9) \\ f(n-10) \end{bmatrix} \quad
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡f(n)f(n−1)f(n−2)f(n−3)f(n−4)f(n−5)f(n−6)f(n−7)f(n−8)f(n−9)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡a0100000000a1010000000a2001000000a3000100000a4000010000a5000001000a6000000100a7000000010a8000000001a9000000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤⋅⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡f(n−1)f(n−2)f(n−3)f(n−4)f(n−5)f(n−6)f(n−7)f(n−8)f(n−9)f(n−10)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
于是,基于矩阵乘法的结合律,可以得到:
[
f
(
n
)
f
(
n
−
1
)
f
(
n
−
2
)
f
(
n
−
3
)
f
(
n
−
4
)
f
(
n
−
5
)
f
(
n
−
6
)
f
(
n
−
7
)
f
(
n
−
8
)
f
(
n
−
9
)
]
=
[
a
0
a
1
a
2
a
3
a
4
a
5
a
6
a
7
a
8
a
9
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
0
0
0
0
0
0
0
0
0
1
0
]
n
−
9
⋅
[
f
(
9
)
f
(
8
)
f
(
7
)
f
(
6
)
f
(
5
)
f
(
4
)
f
(
3
)
f
(
2
)
f
(
1
)
f
(
0
)
]
\begin{bmatrix} f(n) \\ f(n-1) \\ f(n-2) \\ f(n-3) \\ f(n-4) \\ f(n-5) \\ f(n-6) \\ f(n-7) \\ f(n-8) \\ f(n-9) \end{bmatrix} \quad = {\begin{bmatrix} a0 & a1 & a2 & a3 & a4 & a5 & a6 & a7 & a8 & a9 \\ 1&0&0&0&0&0&0&0&0&0\\ 0 & 1 & 0&0&0&0&0&0&0&0 \\ 0&0&1&0&0&0&0&0&0&0\\ 0&0&0&1&0&0&0&0&0&0\\ 0&0&0&0&1&0&0&0&0&0\\ 0&0&0&0&0&1&0&0&0&0\\ 0&0&0&0&0&0&1&0&0&0\\ 0&0&0&0&0&0&0&1&0&0\\ 0&0&0&0&0&0&0&0&1&0\\ \end{bmatrix}}^{ \textstyle n-9} \cdot \begin{bmatrix} f(9) \\ f(8) \\ f(7) \\ f(6) \\ f(5) \\ f(4) \\ f(3) \\ f(2) \\ f(1) \\ f(0) \end{bmatrix} \quad
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡f(n)f(n−1)f(n−2)f(n−3)f(n−4)f(n−5)f(n−6)f(n−7)f(n−8)f(n−9)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡a0100000000a1010000000a2001000000a3000100000a4000010000a5000001000a6000000100a7000000010a8000000001a9000000000⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤n−9⋅⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡f(9)f(8)f(7)f(6)f(5)f(4)f(3)f(2)f(1)f(0)⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤
利用快速幂,可以很快计算出上式中矩阵的(n-9)次方(需要取模),从而解决问题。快速幂计算方法与整数的快速幂基本相同,详见完整代码。
完整代码
#include<iostream>
using namespace std;
int a[10];
int k, m;
struct Matrix{
long long x[10][10];
Matrix()
{
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
x[i][j]=0;
}
}
for(int i=0;i<10;i++) x[0][i]=a[i];
for(int i=1;i<10;i++) x[i][i-1]=1;
}
Matrix operator *(Matrix &M)
{
Matrix res;
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
res.x[i][j]=0;
for(int k=0;k<10;k++)
{
res.x[i][j] += (x[i][k] * M.x[k][j]) % m;
}
res.x[i][j] = res.x[i][j] % m;
}
}
return res;
}
Matrix operator ^(int n)
{
Matrix res, A;
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
if(i == j) res.x[i][j]=1;
else res.x[i][j]=0;
}
}
while(n>0)
{
if(n & 1) res = res * A;
A = A * A;
n >>= 1;
}
return res;
}
};
int main ()
{
while(cin>>k>>m)
{
for(int i=0;i<10;i++) cin>>a[i];
if(k<=9) cout<<k<<endl;
else
{
Matrix M, A;
M = A^(k-9);
int res=0;
for(int i=0;i<9;i++) res += M.x[0][i] * (9-i);
res = res % m;
cout<<res<<endl;
}
}
return 0;
}
D - Q老师染砖
题目大意
有 N 块砖排成一排染色,每一块砖需要涂上红、蓝、绿、黄这 4 种颜色中的其中 1 种。问有多少种方案可以使红色和绿色的块数均为偶数。
输入
第一行为 T,代表数据组数。(1 ≤ T ≤ 100)。
接下来 T 行每行包括一个数字 N,代表有 N 块砖。(1 ≤ N ≤ 1e9)。
输出
输出满足条件的方案数,答案模 10007。
基本思路
这道题可以看成动态规划问题,具体规划方式如下:
状态 | 定义 A [ i ] , B [ i ] , C [ i ] A[i],B[i],C[i] A[i],B[i],C[i]分别为i块砖的情况下,红绿块数全为偶数、全为奇数、一奇一偶的方案数 |
初始化 | A [ 1 ] = 2 , B [ 1 ] = 0 , C [ 1 ] = 2 A[1]=2,B[1]=0,C[1]=2 A[1]=2,B[1]=0,C[1]=2 |
转移过程 | { A [ i ] = 2 × A [ i − 1 ] + C [ i − 1 ] B [ i ] = 2 × B [ i − 1 ] + C [ i − 1 ] C [ i ] = 2 × A [ i − 1 ] + 2 × B [ i − 1 ] + 2 × C [ i − 1 ] \left\{\begin{aligned} A[i]&=&2 \times A[i-1] + C[i-1]\\B[i]&=&2 \times B[i-1] + C[i-1]\\ C[i]&=&2 \times A[i-1] + 2 \times B[i-1] + 2\times C[i-1] \end{aligned} \right. ⎩⎪⎨⎪⎧A[i]B[i]C[i]===2×A[i−1]+C[i−1]2×B[i−1]+C[i−1]2×A[i−1]+2×B[i−1]+2×C[i−1] |
输出答案 | A [ n ] A[n] A[n] |
时间复杂度 | O ( n ) O(n) O(n) |
由于N的范围很大,O(n)的复杂度是无法接受的,可以采用矩阵快速幂进行优化。
矩阵递推式如下所示:
[ A [ i ] B [ i ] C [ i ] ] = [ 2 0 1 0 2 1 2 2 2 ] ⋅ [ A [ i − 1 ] B [ i − 1 ] C [ i − 1 ] ] \begin{bmatrix} A[i] \\ B[i] \\ C[i] \end{bmatrix}= \begin{bmatrix} 2&0&1\\ 0&2&1\\ 2&2&2 \end{bmatrix} \cdot \begin{bmatrix} A[i-1] \\ B[i-1] \\ C[i-1] \end{bmatrix} ⎣⎡A[i]B[i]C[i]⎦⎤=⎣⎡202022112⎦⎤⋅⎣⎡A[i−1]B[i−1]C[i−1]⎦⎤
基于矩阵乘法的结合律,累乘可得:
[
A
[
n
]
B
[
n
]
C
[
n
]
]
=
[
2
0
1
0
2
1
2
2
2
]
n
−
1
⋅
[
A
[
1
]
B
[
1
]
C
[
1
]
]
\begin{bmatrix} A[n] \\ B[n] \\ C[n] \end{bmatrix}= \begin{bmatrix} 2&0&1\\ 0&2&1\\ 2&2&2 \end{bmatrix}^{n-1} \cdot \begin{bmatrix} A[1] \\ B[1] \\ C[1] \end{bmatrix}
⎣⎡A[n]B[n]C[n]⎦⎤=⎣⎡202022112⎦⎤n−1⋅⎣⎡A[1]B[1]C[1]⎦⎤
利用矩阵快速幂求得常数矩阵的n-1次方,从而便可得到答案。
完整代码
#include<iostream>
using namespace std;
#define m 10007
int T;
int N;
int A, B, C;
struct Matrix{
long long x[3][3];
Matrix()
{
x[2][0] = x[2][1] = x[0][0] = x[1][1] = x[2][2] = 2;
x[1][0] = x[0][1] = 0;
x[0][2] = x[1][2] = 1;
}
Matrix operator *(Matrix &M)
{
Matrix R;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
R.x[i][j]=0;
for(int k=0;k<3;k++) R.x[i][j] += (x[i][k] * M.x[k][j]) % m;
R.x[i][j] = R.x[i][j] % m;
}
}
return R;
}
Matrix operator ^(int n)
{
Matrix R, A;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(i == j) R.x[i][j]=1;
else R.x[i][j]=0;
}
}
while(n > 0)
{
if(n & 1) R = R * A;
A = A * A;
n >>= 1;
}
return R;
}
};
int main ()
{
cin>>T;
for(int i=0;i<T;i++)
{
cin>>N;
Matrix R, A;
R = A^(N-1);
cout<<(2*R.x[0][0] + 2*R.x[0][2]) % m<<endl;
}
return 0;
}
E - Q老师度假
题目大意
N天假期,M件衬衫,昨天穿衬衫A,今天穿衬衫B,今天可获得快乐值f[A][B](A,B为编号),求最多可以获得多少快乐值?
输入
输入文件包含多组测试样例,每组测试样例格式描述如下:
第一行给出两个整数 N M,分别代表假期长度与 Q老师 的衬衫总数。(2 ≤ N ≤ 100000, 1 ≤ M ≤ 100)
接下来 M 行,每行给出 M 个整数,其中第 i 行的第 j 个整数,表示 f[i][j]。(1 ≤ f[i][j] ≤ 1000000)
测试样例组数不会超过 10。
输出
每组测试样例输出一行,表示 Q老师 可以获得的最大快乐值。
基本思路
这道题可以用动态规划的方法分析。
状态 | 定义dp[i][j]为前i天假期,最后一天穿第j件衬衫可以获得的快乐值 |
初始化 | d p [ 1 ] [ j ] = 0 ( 0 ⩽ j ⩽ M − 1 ) dp[1][j]=0(0\leqslant j \leqslant M-1) dp[1][j]=0(0⩽j⩽M−1) |
转移过程 | d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j 0 ] + f [ j 0 ] [ j ] } ( 0 ⩽ j 0 ⩽ M − 1 ) dp[i][j]=max\{dp[i-1][j0] + f[j0][j]\}(0 \leqslant j0 \leqslant M-1) dp[i][j]=max{dp[i−1][j0]+f[j0][j]}(0⩽j0⩽M−1) |
输出答案 | m a x { d p [ N ] [ j ] } ( 0 ⩽ j ⩽ M − 1 ) max\{dp[N][j]\}(0\leqslant j \leqslant M-1) max{dp[N][j]}(0⩽j⩽M−1) |
时间复杂度 | O ( N × M × M ) O(N \times M \times M) O(N×M×M) |
时间复杂度太高,直接求搞不定,需要优化。
可以使用矩阵快速幂优化,但是直接使用矩阵快速幂似乎是不可以的,我们需要对矩阵的乘法进行重新的定义:假设矩阵
A
,
B
,
C
A,B,C
A,B,C满足
C
=
A
B
C=AB
C=AB,则
C
[
i
]
[
j
]
=
m
a
x
(
A
[
i
]
[
k
]
+
B
[
k
]
[
j
]
)
C[i][j]=max(A[i][k]+B[k][j])
C[i][j]=max(A[i][k]+B[k][j]),可见,我们把矩阵乘法中的常数乘法换成了加法,常数的加法换成了求max。
实际上,乘法、加法、max都是函数,它们的映射规则不同,但是均满足结合律,并且max对加法可分配。
因此,可以证明,新的矩阵乘法仍然满足使用矩阵快速幂的要求。
矩阵递推式如下:
[
d
p
[
i
]
[
0
]
d
p
[
i
]
[
1
]
⋅
⋅
⋅
d
p
[
i
]
[
M
−
2
]
d
p
[
i
]
[
M
−
1
]
]
=
f
T
⋅
[
d
p
[
i
−
1
]
[
0
]
d
p
[
i
−
1
]
[
1
]
⋅
⋅
⋅
d
p
[
i
−
1
]
[
M
−
2
]
d
p
[
i
]
−
1
[
M
−
1
]
]
\begin{bmatrix} dp[i][0]\\ dp[i][1]\\ ·\\ ·\\ ·\\ dp[i][M-2]\\ dp[i][M-1] \end{bmatrix}=f^T \cdot \begin{bmatrix} dp[i-1][0]\\ dp[i-1][1]\\ ·\\ ·\\ ·\\ dp[i-1][M-2]\\ dp[i]-1[M-1] \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡dp[i][0]dp[i][1]⋅⋅⋅dp[i][M−2]dp[i][M−1]⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=fT⋅⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡dp[i−1][0]dp[i−1][1]⋅⋅⋅dp[i−1][M−2]dp[i]−1[M−1]⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
因此,累乘(新定义的乘法)可得
[
d
p
[
N
]
[
0
]
d
p
[
N
]
[
1
]
⋅
⋅
⋅
d
p
[
N
]
[
M
−
2
]
d
p
[
N
]
[
M
−
1
]
]
=
(
f
T
)
N
−
1
⋅
[
d
p
[
1
]
[
0
]
d
p
[
1
]
[
1
]
⋅
⋅
⋅
d
p
[
1
]
[
M
−
2
]
d
p
[
1
]
[
M
−
1
]
]
\begin{bmatrix} dp[N][0]\\ dp[N][1]\\ ·\\ ·\\ ·\\ dp[N][M-2]\\ dp[N][M-1] \end{bmatrix}=(f^T)^{N-1} \cdot \begin{bmatrix} dp[1][0]\\ dp[1][1]\\ ·\\ ·\\ ·\\ dp[1][M-2]\\ dp[1][M-1] \end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡dp[N][0]dp[N][1]⋅⋅⋅dp[N][M−2]dp[N][M−1]⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤=(fT)N−1⋅⎣⎢⎢⎢⎢⎢⎢⎢⎢⎡dp[1][0]dp[1][1]⋅⋅⋅dp[1][M−2]dp[1][M−1]⎦⎥⎥⎥⎥⎥⎥⎥⎥⎤
利用矩阵快速幂求出 ( f T ) N − 1 (f^T)^{N-1} (fT)N−1,即可求得答案。
需要注意的点
在新定义的矩阵中,单位矩阵是对角线上元素为0,其他位置元素为负无穷的矩阵。
完整代码
#include<iostream>
using namespace std;
#define inf 5000000
int f[105][105];
int M, N;
struct Matrix{
long long x[105][105];
Matrix()
{
for(int i=0;i<M;i++)
{
for(int j=0;j<M;j++)
{
x[i][j] = f[j][i];
}
}
}
Matrix operator *(Matrix &M2)
{
Matrix R;
for(int i=0;i<M;i++)
{
for(int j=0;j<M;j++)
{
R.x[i][j]=0;
for(int k=0;k<M;k++) R.x[i][j] = max(R.x[i][j], x[i][k]+M2.x[k][j]);
}
}
return R;
}
Matrix operator ^(int n)
{
Matrix R, A;
for(int i=0;i<M;i++)
{
for(int j=0;j<M;j++)
{
if(i == j) R.x[i][j] = 0;
else R.x[i][j] = -inf;
}
}
while(n > 0)
{
if(n & 1) R = R * A;
A = A * A;
n>>=1;
}
return R;
}
};
int main ()
{
while(cin>>N>>M)
{
for(int i=0;i<M;i++)
{
for(int j=0;j<M;j++) cin>>f[i][j];
}
Matrix R, A;
R = A^(N-1);
long long res=0;
for(int i=0;i<M;i++)
{
for(int j=0;j<M;j++)
{
res = max(res, R.x[i][j]);
}
}
cout<<res<<endl;
}
return 0;
}