找回密码
 立即注册
首页 业界区 业界 洛谷 P11104 [ROI 2023] 监控 (Day 1) 题解

洛谷 P11104 [ROI 2023] 监控 (Day 1) 题解

醋辛 前天 19:35
题目传送门
题目描述的很清晰,图都配了,就不赘述题面了。
思路分析

问题一:求最小紧凑性

首先可以很容易发现,紧凑性便是以横坐标最大和最小的两个摄像头画面的横轴距离为长、以纵坐标最小和最大的两个摄像头画面的纵轴距离为宽的矩形面积,所以我们只需要让两者尽可能小就行了。
显然,左右的移动和上下移动的这两类操作对于最终答案的贡献是分开的,两者不会互相干扰。以题目第二组样例为例,如图,蓝色部分即为摄像头画面:
1.png

对于此时的图像画面而言,紧凑性为 \(4\times3=12\) 。
执行左移操作,则画面变更为:
2.png

紧凑性变为 \(2\times3=6\) 。
对于以上左移操作,出现变化的只有横坐标最大和最小的两个摄像头画面的横轴距离,而纵向并没有受到左移操作的影响。同理,上下移动操作也不会影响到横向,所以我们可以放心大胆地把横向和纵向分开讨论。
仍以题目第二组样例为例:
先来看横向的。既然我们要求的是横坐标最大和最小的两个摄像头画面的横轴距离,那我们不妨先给所有横坐标进行排序,我们便得到了这么一个序列 \(a\):
3.png

对应到图中,则变成了这样:
4.png

(tips:重复的元素其实无所谓,实际代码中去不去重不影响判断)
设最终答案的横长为 \(X\) 不难发现,当且仅当有元素从一个端点移动到另一个端点时 \(X\) 才会发生变化,其余步骤移动均不会改变 \(X\) 的值。
而从一个端点移动到另一个端点这一步骤,我们既能通过左移来实现,也能通过右移来实现。具体地,如下图,如果我想把左边的画面移动到最右边,既可以通过左移一步来达成,也可以通过右移三步来达成,而其他的画面的位置会跟随其一并发生变化,移动的方式最终不会影响到 \(X\) 的值。
5.png

所以先抛开右移操作,仅考虑左移:发生左右端点间的移动,便是把 \(a\) 中的值最小的点的值变化为 \(h\) ,其余的点的值则减去最小值。由于我们已经给 \(a\) 排好序了,所以其中的最小值便是 \(a_1\) 了,经过左移到左端点后再进行一次左移的操作后,序列 \(a\) 就会变成 $ h,a_2 - a_1 ,\cdots, a_k - a_1$ 。我们再对其排一次序,得到 $ a_2 - a_1 ,\cdots, a_k - a_1,h$
此时,我们再代入求 \(X\) 的公式,便会得到 \(X=h-(a_2-a_1)+1\)。而如果把现在的序列 \(a\) 再进行一次将最小值移动到右端点的操作,我们又能得到一个新的 \(X\) 。可以发现,一共存在 \(K\) 种可能的 \(X\) ,推到以下可得:\(X_i=h-(a_i-a_i-1)+1\ (\ 1 < i \leq k)\) 其中,\(X_i\) 表示以原数组 \(a\) 的第 \(i\) 个数作为最小值时 \(X\) 的值。
带入到样例中,我们能得到如下图所示的序列 \(X\) :
6.png

欸?那 \(X_1\) 怎么办呢?我们只需要在最开始给 \(a\) 排好序的时候直接 \(a_k-a_1\) 就好啦,因为 \(a_1\) 本来就是这么多节点横坐标的最小值嘛:
7.png

于是这样,我们就得到了所有可能的 \(X\) 。
相信这时候就有聪明的同学会想到,题目要求的明明是最小值的紧凑性,那我直接维护最小的 \(X\) 不就行了吗?费这个空间把所有 \(X\) 存下来干嘛?
没错,我们在枚举所有的 \(X\) 时仅需要维护最小的那个就可以了,没必要保留其他那些没什么用处的数据:
8.png

纵向上同理,设最终答案的纵长为 \(Y\) ,同相同的方法挨个枚举可能的 \(Y\) 并记录最小值就行。这样我们就解决了第一个问题。
问题二:达成最小紧凑性时的步数

别忘了题目还有一个问题呢!
前面我们在求最小紧凑性的时候,已经锁定了最小的 \(X\) 和 \(Y\) 的值,并且在枚举两者的时候找到了它们对应的点的坐标位置,那我们不妨在枚举的同时再额外考虑一下如何到达这个问题。
依旧以样例二、横向操作为例:
前文提到,“ 从一个端点移动到另一个端点这一步骤,我们既能通过左移来实现,也能通过右移来实现 ”,具体地,想让数组 \(a\) 中的最小值成为最大值,可以通过将其左移到左界并再次左移,或者是将其一直右移直至右界:
9.png

(素材复用)
但仅仅只有这两种方法了吗?并不一定。
仔细思考可以发现,对于每个 \(X_i\) ,枚举时我们的目的仅仅只是让最左端点移动成为数组 \(a\) 中的最大值,而不一定是让它成为右界 \(h\) 。
样例二并不能很好地体现这一点,我们换一张图:

显然,我们在考虑最左边的 \(a_1\) 右移成为最大值时,其实只需要右移两次、把 \(a_2\) 右移超过右界来到左界就可以了,并不需要把 \(a_1\) 移动到最右端:

所以 让最左端点移动成为数组 \(a\) 中的最大值 这个问题也可以转变为 让最左端点右侧的第一个端点移动成为数组 \(a\) 中的最小值
面对转换后的问题,最简单的方式不就是把 \(a_2\) 这家伙给弄到左端点的位置上去吗?而这又有了左移和右移两种方法。
于是,我们可以得到,对于每个 \(X_i\) ,最简单地达成它一共有四种方式,即四种移动步骤:
1.将 \(a_i\) 左移至左端点的位置再左移一次,步数: \(a_i\) ;
2.将 \(a_i\) 右移至右端点的位置,步数: \(h-a_i\) ;
3.将 \(a_{i+1}\) 左移至左端点的位置,步数: \(a_{i+1}-1\) ;
4.将 \(a_{i+1}\) 右移至右端点的位置再右移一次,步数: \(h-a_{i-1}+1\)
由于题目需要的是最小步数,所以我们就取这四个值里头最小的那个作为 \(X_i\) 对应的步数就可以啦!
纵向也是一个道理,最后输出答案的时候输出最小的 \(X\) 和最小的 \(Y\) 相乘的结果和他们对应的最小步数相加就完工啦!
恭喜你切了这道题!
最后,进点食:
十年OI________,不开________见祖宗!
Code

[code]#includeusing namespace std;const int MAXK=1e5+5;#define int long longint h,w,k,x[MAXK],y[MAXK];      //x[]对应横坐标,即文中的a,y[]对应纵坐标int min_x,min_y,min_stepx=0,min_stepy=0;    //min_x、min_y分别是最小的X和最小的Y;min_stepx和min_stepy则分别对应X和Y最小时的步数signed main(){        cin>>h>>w>>k;        for(int i=1;i>x>>y;        if(k==1)                //这里加了个特判,因为就一个点的时候答案是一定的,再跑一遍程序浪费时间,其实加不加无所谓        {                cout

相关推荐

您需要登录后才可以回帖 登录 | 立即注册