题目:
【问题描述】
给你一个有向图,有 N 个点,标号为 0 到 N -1,图中的每条边有个权值,每次你经过一条边,它的权值将被记入你的得分,如果同样的边被经过多次,它的权值每次都将被记入总分,权值为-499 到 499之间的整数,图中没有自环,现在你要从点 0 到点 1,这项任务必须在时限 T 或 T 之前完成,时间以秒记,每一秒你一定要走 S 步(不多不少),一步的含义是指从当前所在的点 U 沿着某条边( U , V )到达点 V ,若S>1的话,再沿着点V往(V,X)到达点X。你的目标就是完成任务并且得到尽量多的分数。
【输入格式】
第一行一个整数 N 表示该有向图的点数。接下来 N 行,每行有 N 个整数,第 i 行第 j 个数描述了点 i -1 到点 j- 1 之间的边的关系,如果是 0 ,表示它们之间没有边,否则这个数就是 i- > j 这条边的边权,保证第 i 行第 i 个数为 0。最后一行两个整数 S , T ,意义如题目所述。
【输出格式】
仅输出一行,如果不能完成,输出 IMPOSSIBLE,否则输出能够获得的最大分数。
【输入样例】
2
0 1
1 0
3 2
【输出样例】
3
【数据范围】
30%的数据,1≤ N , S , T ≤10。
100%的数据,1≤ N ≤50,1≤ S ≤100,1≤ T ≤109 。
题解:用f[i][j][t]表示从i到j,走t次的最大值
矩阵更新,预处理每两个点走一次的最大值
代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const LL inf=0x3fffffffffffffff; int n,S,T; LL Ans; struct Ma { LL val[51][51]; void clear() { for (int i=0;i<n;i++) for (int j=0;j<n;j++)val[i][j]=-inf; for (int i=0;i<n;i++) val[i][i]=0; } void write() { for (int i=0;i<n;i++) { for (int j=0;j<n;j++)printf("%lld ",val[i][j]); puts(""); } } }M1,M2,M3,M4; Ma times(Ma a,Ma b) { Ma c; for (int i=0;i<n;i++) for (int j=0;j<n;j++) { c.val[i][j] = -inf; for (int k=0;k<n;k++) { if (a.val[i][k]==-inf||b.val[k][j]==-inf) continue; c.val[i][j]=max(c.val[i][j], a.val[i][k]+b.val[k][j]); } } return c; } Ma Pow(Ma a,int b) { Ma Ans; Ans.clear(); for (;b;b>>=1,a=times(a,a)) if (b&1) Ans=times(Ans,a); return Ans; } void solve() { M3=Pow(M2,T-2*n); Ans=max(Ans,M3.val[0][1]); for (int i=T-2*n+1;i<=T;i++) { M3=times(M3,M2); Ans=max(Ans,M3.val[0][1]); } } int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); scanf("%d",&n); for (int i=0;i<n;i++) for (int j=0;j<n;j++) { scanf("%lld",&M1.val[i][j]); if (M1.val[i][j]==0)M1.val[i][j]=-inf; } scanf("%d%d",&S,&T); M2=Pow(M1,S); Ans=-inf; M4.clear(); for (int i=1;i<=min(T,2*n);i++) { M4=times(M4,M2); Ans=max(Ans,M4.val[0][1]); } if (T>=2*n+1) solve(); if (Ans==-inf) puts("IMPOSSIBLE"); else printf("%lld ",Ans); return 0; }