Pro
https://ac.nowcoder.com/acm/problem/239349
Sol
以 f i , j f_{i,j} fi,j表示在第i个世界到底第j个点,最晚可以从哪个世界出发。
状态转移方程:如果一个世界里存在一条x->y的边,那么 f i , y = m a x ( f i − 1 , y , f i − 1 , x ) f_{i,y}=max(f_{i-1,y},f_{i-1,x}) fi,y=max(fi−1,y,fi−1,x),而第一维只与上一个世界有关系,则可以使用滚动数组优化掉。
本题难理解点在于下方代码:f[(i-1)&1][1] = i;Fo(j,1,m) f[i&1][j] = f[(i-1)&1][j];
与f[i&1][1] = i; Fo(j,2,m) f[i&1][j] = f[(i-1)&1][j];
是否等价。
为什么会这么想呢?因为很容易理解的是,第i个世界的第1号点可以由i-1个世界的第1号点得来,就误认为此处f[(i-1)&1][1] = i;
仅仅是对
f
i
,
x
f_{i,x}
fi,x赋初值,所以才会写出后者的代码。
而事实上通过测试样例并仔细调试可以发现,此处不仅仅与赋值有关,且与下方的更新也有关,因此此处的理解可以为:第i个世界可以作为整个关卡的开始,此时在该世界中到达1号点的最晚的世界为i。为什么不考虑以前面世界中的1号节点作为开始呢?其实已经考虑完了:
假设以前面的某个世界的1号节点作为关卡的开始,如果前面的每个世界都选择1号节点,直到选择到第i个世界的1号节点,此时不如直接选择第i个世界的号节点更优;如果前面的每个世界存在一条路径的移动,则到达第i个世界时的节点编号一定不是1,也就是在区间[2,m]
里一定包含了以前面世界中的1号节点作为开始的情况。因此需要一句关键代码f[(i-1)&1][1] = i;
Code
//By cls1277
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define Fo(i,a,b) for(LL i=(a); i<=(b); i++)
#define Ro(i,b,a) for(LL i=(b); i>=(a); i--)
#define Eo(i,x,_) for(LL i=head[x]; i; i=_[i].next)
#define Ms(a,b) memset((a),(b),sizeof(a))
#define endl '\n'
// const LL maxn = ;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
#ifdef DEBUG
freopen("data.txt","r",stdin);
#endif
LL n, m, ans=INT_MAX; cin>>n>>m;
vector<vector<LL>> f(2, vector<LL>(m+1, -INT_MAX));
Fo(i,1,n) {
LL l; cin>>l;
f[(i-1)&1][1] = i;
Fo(j,1,m) f[i&1][j] = f[(i-1)&1][j];
// f[i&1][1] = i;
// Fo(j,2,m) f[i&1][j] = f[(i-1)&1][j];
Fo(j,1,l) {
LL x, y; cin>>x>>y;
f[i&1][y] = max(f[i&1][y], f[(i-1)&1][x]);
if(y==m&&f[i&1][m]!=-INT_MAX) ans = min(ans, i-f[i&1][m]+1);
}
}
cout<<(ans==INT_MAX?-1:ans);
return 0;
}