1 条题解

  • 0
    @ 2025-4-10 13:32:10

    题意分析

    农夫约翰为奶牛建造的障碍场地由 (N) 个平行于 (x) 轴的栅栏组成,栅栏 (i) 的 (y) 坐标为 (i) 。谷仓门在原点 ((0,0)) ,起点在 ((S,N)) 。

    奶牛选择四蹄着地行走,沿栅栏走,到尽头后朝 (x) 轴方向走,碰到其他栅栏或谷仓边缘后再决定左右方向,直到到达谷仓门。

    要求找出奶牛从起点到谷仓门在 (x) 方向上来回行走的最小距离((y) 方向距离固定为 (N) 不计算)。输入包括栅栏数量 (N) 、起点 (x) 坐标 (S) 以及每个栅栏的起始和结束 (x) 坐标。

    解题思路

    1.首先通过线段树记录每个 (x) 坐标位置能到达的最高栅栏编号(因为栅栏按逆序处理)。

    2.从起点到该栅栏两端点的距离 (d[i][0]) 和 (d[i][1]) ,并更新线段树。

    3.定义状态 (f[i][j]) 表示从第 (i) 个栅栏的第 (j) 个端点((j = 0) 或 (j = 1) )出发到谷仓门的最小距离。

    4.通过状态转移方程 (f[to][0] = min(f[to][0], v + abs(p[i][j] - p[to][0]))) 和 (f[to][1] = min(f[to][1], v + abs(p[i][j] - p[to][1]))) 进行状态转移,其中 (v = min(f[i][j], f[i][j^1] + abs(p[i][0] - p[i][1]))) ,(to = d[i][j]) 。

    5.最后 (f[0][0]) 即为所求的最小距离。

    分析

    1.时间复杂度:初始化线段树和处理每个栅栏的操作,时间复杂度为 (O(N \log M)) ,其中 (N) 是栅栏数量,(M) 是 (x) 坐标的范围(这里 (M = 200000) 左右)。状态转移的时间复杂度为 (O(N)) 。所以总的时间复杂度为 (O(N \log M + N) = O(N \log M)) 。

    2.空间复杂度:使用了线段树数组 (a) 、记录栅栏端点的数组 (p) 、记录到达栅栏编号的数组 (d) 、状态数组 (f) 等,空间复杂度为 (O(N + M)) ,其中 (N) 是栅栏数量,(M) 是 (x) 坐标的范围。

    实现步骤

    1.初始化:定义常量和数组,包括线段树数组 (a) 、栅栏端点数组 (p) 、记录到达栅栏编号数组 (d) 、状态数组 (f) 等,将 (f) 数组初始化为较大值。

    2.输入处理:读取栅栏数量 (N) 、起点 (x) 坐标 (S) 以及每个栅栏的起始和结束 (x) 坐标,并对 (x) 坐标进行偏移处理(加 100001)。

    3.线段树操作和距离计算:对于每个栅栏,通过线段树查询从起点到该栅栏两端点能到达的最高栅栏编号 (d[i][0]) 和 (d[i][1]) ,然后更新线段树。

    4.状态初始化:计算起点到最后一个栅栏两端点的距离,初始化 (f[n][0]) 和 (f[n][1]) 。

    5.状态转移:从最后一个栅栏开始逆序遍历,根据状态转移方程更新 (f) 数组。

    6.输出结果:输出 (f[0][0]) ,即从起点到谷仓门在 (x) 方向上来回行走的最小距离。

    代码实现

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define Lc (o<<1)
    #define Rc (o<<1|1)
    const int maxn=50005;
    const int maxidx=200005;
    int n,s,p[maxn][2],d[maxn][2],f[maxn][2];
    int a[maxidx<<2],pl,pr,v;
    inline void pushdown(int o) { a[Lc]=max(a[Lc],a[o]); a[Rc]=max(a[Rc],a[o]); }
    void update(int o,int L,int R)
    {
    	if(pl<=L&&R<=pr) { a[o]=max(a[o],v); return; }
    	pushdown(o);
    	int M=(L+R)>>1;
    	if(pl<=M)
    		update(Lc,L,M);
    	if(pr>M)
    		update(Rc,M+1,R);
    }
    int query(int o,int L,int R,int p,int v)
    {
    	if(L==R) return max(v,a[o]);
    	int M=(L+R)>>1;
    	if(p<=M) return query(Lc,L,M,p,max(v,a[o]));
    	else return query(Rc,M+1,R,p,max(v,a[o]));
    }
    inline void ckmin(int &a,int b) { a=min(a,b); }
    int main()
    {
    #ifdef local
    	freopen("pro.in","r",stdin);
    #endif
    	memset(f,0x3f,sizeof(f));
    	scanf("%d%d",&n,&s);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d%d",&p[i][0],&p[i][1]);
    		p[i][0]+=100001; p[i][1]+=100001;
    		d[i][0]=query(1,1,maxidx-5,p[i][0],0);
    		d[i][1]=query(1,1,maxidx-5,p[i][1],0);
    		// printf("i=%d %d %d\n",i,d[i][0],d[i][1]);
    		v=i; pl=p[i][0]; pr=p[i][1];
    		update(1,1,maxidx-5);
    	}
    	s+=100001;
    	f[n][0]=abs(s-p[n][0]); f[n][1]=abs(s-p[n][1]);
    	p[0][0]=p[0][1]=100001;
    	for(int i=n;i>=1;i--)
    		for(int j=0;j<=1;j++)
    		{
    			// printf("f[%d][%d]=%d\n",i,j,f[i][j]);
    			int v=min(f[i][j],f[i][j^1]+abs(p[i][0]-p[i][1])),to=d[i][j];
    			f[to][0]=min(f[to][0],v+abs(p[i][j]-p[to][0]));
    			f[to][1]=min(f[to][1],v+abs(p[i][j]-p[to][1]));
    		}
    	printf("%d\n",f[0][0]);
    	return 0;
    }
    

    代码说明

    1.头文件和命名空间:包含了 <cstdio> 用于输入输出操作,<cstring> 用于字符串和数组初始化操作,<algorithm> 用于算法相关操作(如 min 函数)。使用 using namespace std; 使代码可以直接使用标准命名空间中的函数和类型。

    2.宏定义和常量定义#define Lc (o<<1)#define Rc (o<<1|1) 用于定义线段树的左右子节点编号。const int maxn = 50005; 定义了栅栏数量的最大值,const int maxidx = 200005; 定义了 (x) 坐标范围相关的最大值。

    3.变量定义n 表示栅栏数量,s 表示起点的 (x) 坐标,p[maxn][2] 存储每个栅栏的两个端点 (x) 坐标,d[maxn][2] 记录从起点到每个栅栏两端点能到达的最高栅栏编号,f[maxn][2] 存储状态信息,a[maxidx<<2] 是线段树数组,plpr 用于线段树更新时记录区间,v 用于记录更新的值。

    4.线段树操作函数pushdown 函数用于将线段树节点的信息下传;update 函数用于更新线段树;query 函数用于查询线段树。

    5.辅助函数ckmin 函数用于更新最小值。

    6.主函数: (1)通过 freopen 函数进行本地文件输入重定向(仅在 local 宏定义存在时)。

    (2)初始化 (f) 数组,读取输入数据并处理 (x) 坐标偏移。

    (3)进行线段树操作和距离计算,更新线段树。

    (4)初始化状态 (f[n][0]) 和 (f[n][1]) 。

    (5)进行状态转移,更新 (f) 数组。

    (6)输出最终结果 (f[0][0]) 。

    • 1

    信息

    ID
    1375
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者