D - It’s a bird! No, it’s a plane! No, it’s AaParsa!
题目描述
n n n 个城市(按顺时针排列成一个环,编号从 0 n − 1 0 \text{~} n-1 0 n−1 )中安装了 m m m 个大炮,每个城市安装了至少一个大炮,第 i i i 个大炮炮口初始指向城市 b i b_i bi ,每过一秒炮口顺时针挪动指向下一个城市,任何东西(包括人)从第 i i i 个城市的炮口发射升空后需要 c i c_i ci 秒到达发射时炮口指向的城市。
现在超人飞不了了,需要通过大炮发射自己来往。对每一对 ( u , v ) (u,v) (u,v) ,求从城市 u u u 到达城市 v v v 最少需要多少秒。
超人可以在任何一个城市停留任意时间。
数据范围与提示
2 ≤ n ≤ 600 , n ≤ m ≤ n 2 , 1 ≤ c i ≤ 1 0 9 2\le n\le 600,n\le m\le n^2,1\le c_i\le 10^9 2≤n≤600,n≤m≤n2,1≤ci≤109 。
思路
题目描述不清楚的可以用百度翻译再看一下原题面。
思路一:
由于炮口的指向只和经过的时间有关系,所以如果我们预处理从每个城市飞一次到达任意一个城市的最短时间(包括飞之前的等待),那么就可以做 D i j Dij Dij 了。
首先这个预处理怎么搞呢?对每个点顺时针做一个简单DP即可。设
m
v
[
i
]
[
j
]
mv[i][j]
mv[i][j] 为从
i
i
i 到
j
j
j 飞一次的最少时间,没有炮口的就原地等炮口挪过来,那么显然有
初始值:
m
v
[
a
i
]
[
b
i
]
=
c
i
转移:
m
v
[
i
]
[
j
]
=
min
(
m
v
[
i
]
[
j
]
,
m
v
[
i
]
[
j
−
1
]
+
1
)
m
v
[
i
]
[
0
]
=
min
(
m
v
[
i
]
[
0
]
,
m
v
[
i
]
[
n
−
1
]
+
1
)
\text{初始值:}mv[a_i][b_i]=c_i \\ \text{转移:}mv[i][j]=\min(mv[i][j],mv[i][j-1]+1) \\ mv[i][0]=\min(mv[i][0],mv[i][n-1]+1)
初始值:mv[ai][bi]=ci转移:mv[i][j]=min(mv[i][j],mv[i][j−1]+1)mv[i][0]=min(mv[i][0],mv[i][n−1]+1)
循环转移即可。
但是这个 m v mv mv 值是默认到达点 i i i 时间为第 0 0 0 秒的,怎么用在 D i j Dij Dij 上呢?其实转动模拟一下就可以发现,第 s s s 秒到达点 i i i 、从 i i i 到 j j j 的最短时间等于第 0 0 0 秒到达点 i i i 、从 i i i 到 ( j − s ) m o d n (j-s)\bmod n (j−s)modn 的最短时间,然后用 m v mv mv 做边权跑一遍 O ( n 2 ) O(n^2) O(n2) 的 D i j Dij Dij 即可。
由于每个点要求一遍单源点最短路,所以复杂度 O ( n 3 ) O(n^3) O(n3) 。
谨告:以后不要再用 Floyd \text{Floyd} Floyd 了, Floyd \text{Floyd} Floyd 在 D i j Dij Dij 面前毫无优点(常数小些?卡卡就一样了),而且 Floyd \text{Floyd} Floyd 能做的题 D i j Dij Dij 一定都能做, D i j Dij Dij 能做的 Floyd \text{Floyd} Floyd 不一定能做。
拿这题来说,由于路径并没有可合并的性质,边权要根据前一个点的最短路变化,所以用
Floyd
\text{Floyd}
Floyd 会 WA
。
思路二:
官方做法。
大致思路是和思路一差不多的,但是在处理“等待任意时间”这个条件时不用预处理DP,而是对每个 i i i ,向 ( i + 1 ) m o d n (i+1)\bmod n (i+1)modn 连一条长度为1的伪边。伪边不旋转,实边(即大炮所构成的边)要旋转。
这个解法多了一个特判,但是理论上边数 = m + n ≤ n 2 + n =m+n\le n^2+n =m+n≤n2+n ,时间应该更优。
代码
思路一:
#include<cstdio>//JZM YYDS!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#define ll long long
#define MAXN 605
#define uns unsigned
#define MOD 998244353ll
#define INF 0x7f7f7f7f
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
int n,m;
ll dp[MAXN][MAXN],mv[MAXN][MAXN];
bool vis[MAXN];
signed main()
{
n=read(),m=read();
memset(dp,0x7f,sizeof(dp));
memset(mv,0x7f,sizeof(mv));
for(int i=1;i<=m;i++){
int a=read(),b=read();
mv[a][b]=read();
}
for(int i=0;i<n;i++){
for(int j=1;j<n;j++)mv[i][j]=min(mv[i][j],mv[i][j-1]+1);
mv[i][0]=min(mv[i][0],mv[i][n-1]+1);
for(int j=1;j<n;j++)mv[i][j]=min(mv[i][j],mv[i][j-1]+1);
mv[i][0]=min(mv[i][0],mv[i][n-1]+1);
}
for(int x=0;x<n;x++){
dp[x][x]=0,dp[x][n]=INF;
for(int i=0;i<n;i++)vis[i]=0;
for(int K=0;K<n;K++){
int u=n;
for(int i=0;i<n;i++)
if(!vis[i]&&dp[x][i]<dp[x][u])u=i;
if(u==n)break;vis[u]=1;
int ad=((-dp[x][u])%n+n)%n;
for(int i=0;i<n;i++)if(!vis[i])
dp[x][i]=min(dp[x][i],dp[x][u]+mv[u][(i+ad)%n]);
}
}
for(int i=0;i<n;i++){
dp[i][i]=0;
for(int j=0;j<n;j++)printf("%lld ",dp[i][j]);
putchar('\n');
}
return 0;
}
思路二(官方代码):
#include <bits/stdc++.h>
#pragma GCC optimize ("O2,unroll-loops")
//#pragma GCC optimize("no-stack-protector,fast-math")
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<pii, int> piii;
typedef pair<ll, ll> pll;
#define debug(x) cerr<<#x<<'='<<(x)<<endl;
#define debugp(x) cerr<<#x<<"= {"<<(x.first)<<", "<<(x.second)<<"}"<<endl;
#define debug2(x, y) cerr<<"{"<<#x<<", "<<#y<<"} = {"<<(x)<<", "<<(y)<<"}"<<endl;
#define debugv(v) {cerr<<#v<<" : ";for (auto x:v) cerr<<x<<' ';cerr<<endl;}
#define all(x) x.begin(), x.end()
#define pb push_back
#define kill(x) return cout<<x<<'\n', 0;
const int inf=1000000010;
const ll INF=1000000000000001000LL;
const int mod=1000000007;
const int N=605;
int n, m, k, u, v, x, y, t, a, b;
bool mark[N];
int G[N][N], D[N];
inline void upd(int &x, int y){ if (x>y) x=y;}
int main(){
ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
memset(G, 63, sizeof(G));
cin>>n>>m;
while (m--){
cin>>u>>v>>x;
G[u][v]=min(G[u][v], x);
}
for (int i=0; i<n; i++){
memset(D, 63, sizeof(D));
memset(mark, 0, sizeof(mark));
for (int v=0; v<n; v++) upd(D[v], G[i][v]);
while (1){
int v=-1;
for (int x=0; x<n; x++) if (!mark[x]){
if (v==-1 || D[v]>D[x]) v=x;
}
if (v==-1) break ;
mark[v]=1;
upd(D[(v+1)%n], D[v]+1);
for (int u=0; u<n; u++)
upd(D[(u+D[v])%n], D[v]+G[v][u]);
}
for (int j=0; j<n; j++){
if (i==j) cout<<"0 ";
else cout<<D[j]<<" ";
}
cout<<"\n";
}
return 0;
}