1 条题解
-
0
题意分析
本题设定在农夫约翰为给山脊上的苜蓿浇水而安装洒水器的场景中。
首先,山脊被视作一条长度为(L)((1≤ L≤1000000),且(L)为偶数)的一维数轴。洒水器喷头需安装在山脊上,其洒水半径为整数,范围在(A)至(B)((1≤ A≤ B≤1000))之间,这意味着每个洒水器能向两侧一定整数距离内洒水,且要保证山脊上每个位置都被且仅被一个洒水器覆盖,同时不能向山脊两端之外洒水。
另外,有(N)头奶牛((1≤N≤1000)),每头奶牛都有其特别喜欢的苜蓿生长范围,这个范围由闭区间((S, E))确定((0≤S< E≤ L))。并且,每头奶牛喜欢的范围必须由一个洒水器来浇灌,该洒水器的洒水范围可以超出或不超出此区间。
题目的目标是求出覆盖整个山脊且不重叠的最少洒水器数量。若无法设计出符合所有条件的洒水器配置方案,则输出(-1)。
在输入方面,第一行输入两个整数(N)和(L),分别代表奶牛数量和山脊长度;第二行输入洒水半径范围(A)和(B);后续(N)行,每行输入两个整数(S)和(E),用于确定每头奶牛喜欢的苜蓿范围的起止位置。输出则只需给出满足条件的最少洒水器数量,若方案不存在,输出(-1)。例如给定输入“(2 8);(1 2);(6 7);(3 6)”,经分析计算可知最少需要(3)个洒水器,输出即为(3) 。
解题思路:
1.首先将洒水半径(A)和(B)乘以(2),这样处理是为了将双向洒水转化为单向考虑(因为在一维数轴上,双向洒水的总覆盖长度等于单向覆盖长度的(2)倍)。
2.初始化数组(f),用于记录每个位置覆盖所需的最少洒水器数量,初始值设为一个较大的值(inf),并将(f[0])设为(0)(表示起点不需要额外的洒水器)。
3.遍历每头奶牛喜欢的范围,将该范围内的位置对应的(f)值设为一个更大的值((inf + 1)),表示这些位置不能被其他洒水器覆盖(因为要保证每头奶牛喜欢的范围由单个洒水器覆盖)。
4.对于在(A)到(B)范围内且(f)值小于等于(inf)的位置,将其(f)值设为(1),表示这些位置可以用一个洒水器覆盖。
5.使用一个队列(通过数组(p)和指针(he)、(ta)模拟)来维护当前可以作为下一个洒水器安装位置的候选位置。
6.遍历从(b)到(m)的位置,对于每个位置(i):
7.如果(i)不是(b),将(i - a)插入队列。
8.当(i - p[he] > b)时,说明(p[he])对应的位置已经超出了当前位置(i)的可覆盖范围,将(he)后移。
9.如果(f[i] \leq inf),则更新(f[i])为(f[p[he]] + 1),表示从(p[he])位置到(i)位置需要额外一个洒水器。
10.最后,如果(f[m] \geq inf),说明无法覆盖整个山脊,输出(-1);否则输出(f[m])。
分析
1.时间复杂度:输入部分读取数据的时间复杂度为(O(N))。初始化和处理奶牛喜欢范围的时间复杂度为(O(N × (E - S))),由于(E - S ≤ L),所以这部分时间复杂度为(O(N × L))。后续遍历位置并更新(f)值的时间复杂度为(O(L))。整体时间复杂度为(O(N × L + L) = O((N + 1) × L))。
2.空间复杂度:使用了数组(f)和(p),数组大小为(1000001),所以空间复杂度为O(L),因为(L ≤ 1000000)。
实现步骤:
1.输入读取:通过自定义的(read)函数读取(N)、(L)、(A)、(B)以及每头奶牛喜欢的范围的起始和结束位置(S)、(E)。
2.初始化和预处理:将(A)和(B)乘以(2);初始化数组(f),将其所有元素设为较大值(inf),并将(f[0])设为(0);遍历每头奶牛喜欢的范围,将范围内的(f)值设为(inf + 1);对于(A)到(B)范围内且(f)值小于等于(inf)的位置,将其(f)值设为(1)。
3.队列初始化和处理:初始化队列指针(he = 1),(ta = 0);对于(0)到(b - a)范围内的位置,将其插入队列。
4.位置遍历和更新:从(b)到(m)遍历位置,根据上述解题思路中的步骤进行队列操作和(f)值的更新。
5.结果输出:如果(f[m] \geq inf),输出(-1);否则输出(f[m])。
代码实现
#include<cstdio> #include<cstring> int n,m,a,b,x,y,he,ta,f[1000001],p[1000001],inf; bool flag; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } void insert(int u) { while(he<=ta && f[p[ta]]>f[u]) ta--; p[++ta]=u; while(u-p[he]>b) he++; } int main() { n=read();m=read();a=read()*2;b=read()*2; memset(f,127/3,sizeof(f));inf=f[0];f[0]=0; for(int i=1;i<=n;i++) { x=read(),y=read(); for(int j=x+1;j<y;j++) f[j]=inf+1; } for(int i=a;i<=b;i+=2) if(f[i]<=inf) f[i]=1; for(int i=0;i<=b-a;i+=2) insert(i); for(int i=b;i<=m;i+=2) { if(i!=b) insert(i-a); while(i-p[he]>b) he++; if(f[i]<=inf) f[i]=f[p[he]]+1; } f[m]=f[m]>=inf ? -1:f[m]; printf("%d\n",f[m]); return 0; }
代码说明:
1.头文件:包含了
<cstdio>
用于输入输出操作,<cstring>
用于字符串和数组的初始化操作。2.变量定义: (1)
n
表示奶牛的数量,m
表示山脊的长度,a
和b
表示洒水半径的范围(乘以(2)后),x
和y
用于存储每头奶牛喜欢的范围的起始和结束位置,he
和ta
用于模拟队列的头指针和尾指针,f
数组用于记录每个位置覆盖所需的最少洒水器数量,p
数组用于模拟队列存储候选位置,inf
表示一个较大的值。(2)
flag
变量未在代码中使用(可能是代码编写过程中的遗留变量)。(3)
read
函数:用于读取整数输入,通过字符逐个处理,将字符转换为整数并处理正负号。(4)
insert
函数:用于将位置(u)插入队列,同时维护队列的性质,即队列中位置对应的(f)值是单调递减的,并且当位置超出当前可覆盖范围时,将头指针后移。3.主函数:
(1)读取输入数据并进行预处理操作,包括将(A)和(B)乘以(2),初始化(f)数组等。
(2)遍历每头奶牛喜欢的范围,更新(f)数组。
(3)初始化队列并处理(A)到(B)范围内的位置。
(4)遍历从(b)到(m)的位置,进行队列操作和(f)值的更新。
(5)根据(f[m])的值输出结果,如果(f[m] \geq inf),输出(-1);否则输出(f[m])。
- 1
信息
- ID
- 1374
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 2
- 上传者