题目:点击打开链接
题意:有n天,一个人一天可以选择穿一种T恤,如果今天穿A,明天穿B,可以获得f[A][B]的快乐值,问这n天可以获得的最大快乐值。f为m*m的矩阵。(2≤N≤100000,1≤M≤100)
分析:
这题涉及到矩阵乘法以及快速幂,首先我们先利用dp来做
dp[i][j][k]表示从i走到j,走k步的最大距离,那么转移就是
dp[i][j][k] = max(dp[i][j][k], dp[i][t][k-1] + dis[t][j]) (t是所有其他点,dis表示t到j的边的距离)。直接这样做肯定是TLE的。
首先我们先把它转换成类似矩阵乘法的形式,我们定义矩阵运算符,以及矩阵内元素间的两个运算符*和+,AB的结果为C,那么定义做的实际运算内容为:
因为max和+也是2个二元运算,不妨把max(a,b)的运算符改为+,a+b改为*,那么转移方程就是 dp[i][j][k] = sigma(dp[i][t][k-1]*dis[t][j])
我们就得到了矩阵乘法类似的形式。
又因为max对于加法是可分配的,即max(a+b,a+c)=a+max(b,c),max和加法本身都是可以交换和结合的,所以对应的矩阵乘法也满足结合律。
所以可以使用快速幂优化,复杂度为O(m^3logn)
代码:
#include <map>
#include <cmath>
#include <cstdio>
#include <ctime>
#include <string>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <utility>
#include <iostream>
#include <algorithm>
#define LL long long
#define pi 3.1415926535897932384626433
#define sqr(a) ((a)*(a))
using namespace std;
typedef pair<int,int> PII;
const int N=101;
int n,k,m,P;
LL a[N][N],b[N][N],tmp[N][N];
void cal(LL c[][N],LL a[][N],LL b[][N]){
int i,j,k;
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
tmp[i][j]=0;
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
for (k=1;k<=m;k++)
tmp[i][j]=max(tmp[i][j],a[i][k]+b[k][j]);
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
c[i][j]=tmp[i][j];
}
void pr(LL a[][N]){
printf("\n");
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
if (j<m) printf("%lld ",a[i][j]);else printf("%lld\n",a[i][j]);
}
LL solve(int n){
for (;n;n/=2){
if (n%2) cal(b,b,a);
cal(a,a,a);
}
LL ans=0;
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
if (b[i][j]>ans) ans=b[i][j];
return ans;
}
int main(){
int i,j,k;
while (scanf("%d%d",&n,&m)!=EOF){
for (i=1;i<=m;i++)
for (j=1;j<=m;j++)
scanf("%lld",&a[i][j]),b[i][j]=0;
printf("%lld\n",solve(n-1));
}
return 0;
}