题目大意:
给定一张有向图。每条边可以有这样的操作:如果一条边a->b=c,即从a指向b,边权为c。那么有a->(b+1)%n=c+1,即可以从a指向(b+1)%n=c+1。即输入的图只是初始状态,每条边每间隔1s都会指向不同的终点。要求在这张图上任意两点的最短距离。
思路:
此题唯一不容易处理的是“等待”这个操作,那么先考虑不等待的情况,
If(Graph[i][j]){ //I与j相连
To=d[i]+j //即走到这条边时,终点的位置
d[to]=min(d[to],d[i]+Graph[i][j]); //用dijkstra的方法进行拓展
}
那么如果有等待操作呢?
假设0到1有一条权值位2的边,那么下一秒,就有一条指向2的权值为3的边,依此类推。并且对于每一个点实时保存这些信息会MLE和TLE的。所以不能将这些边真的保存下来再访问(例如分层图和DP都不行)。那么需要将情况进行简化,不能每一个点都要等待N秒。即如果0与1有一边,那么同时更新d[1]的值和d[2]的值,d[2]=min(d[2],d[1]+1),意味着如果已经得到d[1]的最小值,那么在到达1之前等待1s就可以到2了。
那么这样的简化会不会漏掉情况,剩下的0-3,0-4都没有更新啊。其实这只是暂时的,当到达2时,同时也会更新3。到达3时也会更新4。那么还有一个问题,这里只讨论了0的等待,那么1如果有边也可以等待啊。我们想一想,如果达到1之后再等1s才能到达2,那么我们也可以在到达1之前等1s再到2,答案上是更优的(因为1->2的边权值是正值)。那么如果到达1之后再等2s才能到达2呢,那么这是不必要的,因为同样,我们可以在到达1之前等1s再到2,这个答案更优。所以只讨论起点会进行等待就能得到最优答案。
关于代码:
那么综上,所有情况讨论完了。再讨论一个问题,用什么跑最短路,题目给出边的最大值是 M<N2 ,那么此时如果跑优先队列优化的dijkstra就会有N*MlogN= N3 logN的时间复杂度,而普通版本是 N2 的dijkstra总时间复杂度是N^3。所以跑稠密图就不要优化了。此题不能用Floyd跑的原因在于图不是唯一确定的,而dijkstra可以实时的维护图的状态,并求出最短路。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=600+10;
long long int G[MAXN][MAXN];
long long int d[MAXN];
bool v[MAXN];
int n,m;
void dijkstra(int s){
memset(v,0,sizeof(v));
long long int maxx=1e12;
for(int i=0;i<n;i++)d[i]=maxx;
for(int i=0;i<n;i++){
if(G[s][i]){
d[i]=min(d[i],G[s][i]);
}
}
while(1){
int p=-1;
maxx=1e12;
for(int i=0;i<n;i++){
if(!v[i]&&maxx>d[i]){
maxx=d[i];
p=i;
}
}
if(p==-1)break;
v[p]=1;
d[(p+1)%n]=min(d[(p+1)%n],d[p]+1);
for(int i=0;i<n;i++){
if(G[p][i]){
int r=(d[p]+i)%n;
d[r]=min(d[r],d[p]+G[p][i]);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int f,t,c;
scanf("%d%d%d",&f,&t,&c);
G[f][t]=c;
}
for(int i=0;i<n;i++){
dijkstra(i);
for(int j=0;j<n;j++){
if(i==j)printf("0 ");
else printf("%lld ",d[j]);
}
printf("\n");
}
}