1 条题解

  • 0
    @ 2025-8-24 23:08:10

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar DJN123
    若是人生无悔,那该多无趣啊

    搬运于2025-08-24 23:08:10,当前版本为作者最后更新于2025-04-13 17:02:20,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    看前面两篇题解有些地方没有说清楚,来发一篇解释一下这个思路的可行性。

    题意

    有一个 nn 个点,mm 条边的无向连通图,每个点有一定的高度要求,起点高度为零,每分钟可以经过一条边或原地不动,并同时使高度加一或减一或保持不变,并要求道达终点时高度为零。求:从 11nn 的最少耗时。

    思路

    观察题目,我们可以发现每一分钟可以改变 11 的高度,也就是说,我们要到达 aia_i 的高度,所需时间最小aia_i(就是一直上升的情况),也就是说,我们可以得到结论:在一直上升的情况下,高度就等于所花费时间

    但是,这个结论显然没有达到我们的要求。因为一条路径要求到终点时高度为零,因此我们不可能一直上升。但是也不难得出,在路径经过最高点后,我们就不再上升了。

    举个栗子

    这是作者画的丑图

    绿色这一段一直上升,而蓝色一段则需要一直下降,交汇的地方就是最高点,那么最高点高度 hh 就满足:总时间 tt=2×ht_t = 2 \times h
    怎么来的呢?绿色这一段的时间等于 hh,这可以通过上述结论得来,而蓝色这一段,我们可以把它看成从终点上升到最高点,那么就符合条件限制了。


    但我们肯定不可能去求一个路径的最高点,于是,我们发现,我们可以通过到达某个点的时间来设定这个路径的最高点,前面我们得到时间可以等于高度,那么我们对于任意一点 ii,从起点一直上升到 ii 的时间为 d1id1_i,从终点一直上升到 ii 的时间为 d2id2_i 于是我们可以得到路径的最高点为 max(d1i,d2i)\max(d1_i , d2_i) 所以总耗时应该为 max(d1i,d2i)×2\max(d1_i, d2_i) \times 2

    解释一下

    不难看出,由于两条路径的时间差,我们到达 ii 的高度就有差异(图中金色虚线),所以我们用 d1id2i\left | d1_i - d2_i \right | 来表示高度差,于是我们可以通过第 ii 个点求出路径时间为 d1i+d2i+d1id2id1_i + d2_i + \left | d1_i - d2_i \right |max(d1i,d2i)×2\max(d1_i , d2_i) \times 2
    这个思路还有没有缺陷呢?当然有了。

    比如这个

    这个不是作者画的丑图

    诶我们发现你头顶怎么尖尖的,当这条路径的长度(不是耗时),为奇数时,会出现这么个“尖顶”——(就是一段上升和一段竖直下降),这玩意的耗时为 22 但如果我们用一段平飞来代替就可以使总耗时减一。怎么解决呢?我们可以“手动”加边,就是分别让 iii1i - 1“分担”一半,然后总耗时加上我们平飞的边,具体实现就是在取完 max(d1i,d2i)×2 \max(d1_i , d2_i) \times 2 后,再找 ii 的连点 rr,取 max(d1i,d2r)×2+1 \max(d1_i , d2_r) \times 2 + 1 就解决了。
    完结散花。

    Code:

    #include<bits/stdc++.h>
    const int N = 200005, M = 400005 * 2;
    using namespace std;
    int in(){
    	int ans = 0, f = 1;
    	char c = getchar();
    	while(c < '0' || '9' < c){
    		if(c == '-')f = -1;
    		c = getchar();
    	}
    	do{
    		ans = (ans << 3) + (ans << 1) + c - '0';
    		c = getchar();
    	}while('0' <= c && c <= '9');
    	return ans * f; 
    }
    struct node{
    	int next, to;
    }road[M];
    int head[N], cnt;
    void add(int x, int y){
    	road[++cnt].next = head[x];
    	road[cnt].to = y;
    	head[x] = cnt;
    }
    int d1[N], d2[N], a[N];
    bool vis[N];
    int n, m, u, v;
    void spfa(int u, int f[]){ 
    	memset(vis, 0, sizeof(vis));
    	f[u] = 0;
    	queue<int>q;
    	q.push(u);
    	while(!q.empty()){
    		int h = q.front();
    		q.pop();
    		for(int i = head[h];i;i = road[i].next){
    			int r = road[i].to, ext = max(a[r] - f[h] - 1, 0);
    			if(f[r] > f[h] + ext + 1){
    				f[r] = f[h] + ext + 1;
    				if(!vis[r]){
    					q.push(r);
    					vis[r] = 1;
    				}
    			}
    		}
    		vis[h] = 0;
    	}
    }
    signed main(){
    	n = in(), m = in();
    	for(int i = 1;i <= n;i++)a[i] = in();
    	for(int i = 1;i <= m;i++){
    		u = in(), v = in();
    		add(u, v), add(v, u);
    	}
    	memset(d2, 63, sizeof(d2));
    	memset(d1, 63, sizeof(d1));
    	spfa(1, d1);
    	spfa(n, d2);
    	int ans = INT_MAX;
    	for(int i = 1;i <= n;i++){
    		ans = min(max(d1[i] , d2[i]) * 2, ans);
    		for(int j = head[i];j;j = road[j].next){
    			int r = road[j].to;
    			ans = min(max(d1[i], d2[r]) * 2 + 1, ans);
    		}
    	}
    	cout<<ans;
    }
    
    • 1

    信息

    ID
    11295
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者