思路分析
问题一:求最小紧凑性
首先可以很容易发现,紧凑性便是以横坐标最大和最小的两个摄像头画面的横轴距离为长、以纵坐标最小和最大的两个摄像头画面的纵轴距离为宽的矩形面积,所以我们只需要让两者尽可能小就行了。
显然,左右的移动和上下移动的这两类操作对于最终答案的贡献是分开的,两者不会互相干扰。以题目第二组样例为例,如图,蓝色部分即为摄像头画面:
对于此时的图像画面而言,紧凑性为
。
执行右移操作,则画面变更为:
紧凑性变为
。
对于以上左移操作,出现变化的只有横坐标最大和最小的两个摄像头画面的横轴距离,而纵向并没有受到左移操作的影响。同理,上下移动操作也不会影响到横向,所以我们可以放心大胆地把横向和纵向分开讨论。
仍以题目第二组样例为例:
先来看横向的。既然我们要求的是横坐标最大和最小的两个摄像头画面的横轴距离,那我们不妨先给所有横坐标进行排序,我们便得到了这么一个序列
:
对应到图中,则变成了这样:
(tips:重复的元素其实无所谓,实际代码中去不去重不影响判断)
设最终答案的横长为
,不难发现,当且仅当有元素从一个端点移动到另一个端点时
才会发生变化,其余步骤移动均不会改变
的值。
而从一个端点移动到另一个端点这一步骤,我们既能通过左移来实现,也能通过右移来实现。具体地,如下图,如果我想把左边的画面移动到最右边,既可以通过左移一步来达成,也可以通过右移三步来达成,而其他的画面的位置会跟随其一并发生变化,移动的方式最终不会影响到
的值。
所以先抛开右移操作,仅考虑左移:发生左右端点间的移动,便是把
中的值最小的点的值变化为
,其余的点的值则减去最小值。由于我们已经给
排好序了,所以其中的最小值便是
了,经过左移到左端点后再进行一次左移的操作后,序列
就会变成
。我们再对其排一次序,得到
此时,我们再代入求
的公式,便会得到
。而如果把现在的序列
再进行一次将最小值移动到右端点的操作,我们又能得到一个新的
。可以发现,一共存在
种可能的
,推到以下可得:
其中,
表示以原数组
的第
个数作为最小值时
的值。
带入到样例中,我们能得到如下图所示的序列
:
欸?那
怎么办呢?我们只需要在最开始给
排好序的时候直接
就好啦,因为
本来就是这么多节点横坐标的最小值嘛:
于是这样,我们就得到了所有可能的
。
相信这时候就有聪明的同学会想到,题目要求的明明是最小值的紧凑性,那我直接维护最小的
不就行了吗?费这个空间把所有
存下来干嘛?
没错,我们在枚举所有的
时仅需要维护最小的那个就可以了,没必要保留其他那些没什么用处的数据:
纵向上同理,设最终答案的纵长为
,同相同的方法挨个枚举可能的
并记录最小值就行。这样我们就解决了第一个问题。
问题二:达成最小紧凑性时的步数
别忘了题目还有一个问题呢!
前面我们在求最小紧凑性的时候,已经锁定了最小的
和
的值,并且在枚举两者的时候找到了它们对应的点的坐标位置,那我们不妨在枚举的同时再额外考虑一下如何到达这个问题。
依旧以样例二、横向操作为例:
前文提到,“ 从一个端点移动到另一个端点这一步骤,我们既能通过左移来实现,也能通过右移来实现 ”,具体地,想让数组
中的最小值成为最大值,可以通过将其左移到左界并再次左移,或者是将其一直右移直至右界:
(素材复用)
但仅仅只有这两种方法了吗?并不一定。
仔细思考可以发现,对于每个
,枚举时我们的目的仅仅只是让最左端点移动成为数组
中的最大值,而不一定是让它成为右界
。
样例二并不能很好地体现这一点,我们换一张图:
显然,我们在考虑最左边的
右移成为最大值时,其实只需要右移两次、把
右移超过右界来到左界就可以了,并不需要把
移动到最右端:
所以 让最左端点移动成为数组
中的最大值 这个问题也可以转变为 让最左端点右侧的第一个端点移动成为数组
中的最小值。
面对转换后的问题,最简单的方式不就是把
这家伙给弄到左端点的位置上去吗?而这又有了左移和右移两种方法。
于是,我们可以得到,对于每个
,最简单地达成它一共有四种方式,即四种移动步骤:
1.将
左移至左端点的位置再左移一次,步数:
;
2.将
右移至右端点的位置,步数:
;
3.将
左移至左端点的位置,步数:
;
4.将
右移至右端点的位置再右移一次,步数:
由于题目需要的是最小步数,所以我们就取这四个值里头最小的那个作为
对应的步数就可以啦!
纵向也是一个道理,最后输出答案的时候输出最小的
和最小的
相乘的结果和他们对应的最小步数相加就完工啦!
恭喜你切了这道题!
最后,进点食:
十年OI________,不开________见祖宗!
Code
#include<bits/stdc++.h>
using namespace std;
const int MAXK=1e5+5;
#define int long long
int 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<=k;i++)
cin>>x[i]>>y[i];
if(k==1) //这里加了个特判,因为就一个点的时候答案是一定的,再跑一遍程序浪费时间,其实加不加无所谓
{
cout<<"1 0";
return 0;
}
sort(x+1,x+1+k);
sort(y+1,y+1+k);
min_x=x[k]-x[1]+1;
min_y=y[k]-y[1]+1;
for(int i=1;i<=k;i++)
{
if(min_x==(h-(x[i]-x[i-1])+1))
min_stepx=min(min_stepx,min(x[i-1],min(h-x[i-1],min((x[i]-1),(h-x[i]+1)))));
else
{
min_x=min(min_x,(h-(x[i]-x[i-1])+1));
if(min_x==(h-(x[i]-x[i-1])+1))
min_stepx=min(x[i-1],min(h-x[i-1],min((x[i]-1),(h-x[i]+1))));
}
if(min_y==(w-(y[i]-y[i-1])+1))
min_stepy=min(min_stepy,min(y[i-1],min(w-y[i-1],min((y[i]-1),(w-y[i]+1)))));
else
{
min_y=min(min_y,(w-(y[i]-y[i-1])+1));
if(min_y==(w-(y[i]-y[i-1])+1))
min_stepy=min(y[i-1],min(w-y[i-1],min((y[i]-1),(w-y[i]+1))));
}
}
cout<<(min_x*min_y)<<" "<<(min_stepx+min_stepy);
return 0;
}