【补题日记】[2022牛客暑期多校2]L-Link with Level Editor I

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(fi1,y,fi1,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
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;
}

你可能感兴趣的:(补题日记,算法,c++,图论)