社交距离 I

社交距离 I

一种新型疾病,COWVID-19,开始在全世界的奶牛之间传播。

Farmer John 正在采取尽可能多的预防措施来防止他的牛群被感染。

Farmer John 的牛棚是一个狭长的建筑物,有一排共 N 个牛栏。

有些牛栏里目前有奶牛,有些目前空着。

得知“社交距离”的重要性,Farmer John 希望使得 D 尽可能大,其中 D 为最近的两个有奶牛的牛栏的距离。

例如,如果牛栏 38 是最近的有奶牛的牛栏,那么 D=5

最近两头奶牛新来到 Farmer John 的牛群,他需要决定将她们分配到哪两个之前空着的牛栏。

请求出他如何放置这两头新来的奶牛,使得 D 仍然尽可能大。

Farmer John 不能移动任何已有的奶牛;他只想要给新来的奶牛分配牛栏。

输入格式

输入的第一行包含 N

下一行包含一个长为 N 的字符串,由 01 组成,描述牛棚里的牛栏。

0 表示空着的牛栏,1 表示有奶牛的牛栏。

字符串中包含至少两个 0,所以有足够的空间安置两头新来的奶牛。

输出格式

输出 Farmer John 以最优方案在加入两头新来的奶牛后可以达到的最大 D 值(最近的有奶牛的牛栏之间的距离)。

数据范围

2N105

输入样例:

14
10001001000010

输出样例:

2

样例解释

在这个例子中,Farmer John 可以以这样的方式加入奶牛,使得牛栏分配变为 10x010010x0010,其中 x 表示新来的奶牛。

此时 D=2

不可能在加入奶牛之后取到更大的 D 值。

 

解题思路

  分情况来讨论,一共有两种情况。

  1. 两头牛在同一个区间。
  2. 两头牛在不同的区间。

  我们把首尾区间和中间的区间分开来看。

  对于首尾区间,如果要把两头牛都放在首区间中,很明显,当把一头牛放在左端点时,另一头牛距离左右两头牛的距离才会尽可能的大。那么另一头牛应该放在什么地方?假设这头牛距离左端点的牛的距离为d1,距离x1的距离为d2,那么应该有d1+d2=x11。当d1d2越接近时,d1d2中的最小值才会越大,我们让d1=d2=x112。如果得到的距离是整数的话,那么d1d2取相等。如果不是整数,那么下取整,比如5,那么会得到一个是2,另一个是3,我们应该取2

  尾区间同理,最小的距离应该是nxm2xm是序列中最后一头牛的下标,在上面的序列中m=5

  对于中间的区间[xi,xi+1],当放入两头牛后会有三段距离,一段是一头牛与区间左边的牛的距离d1,一段是两头牛之间的距离d2,一段是另一头牛与区间右边的牛的距离d3。同理会有d1+d2+d3=xi+1xi。要使这三个数的最小值最大,应该有让d1=d2=d3,假设d1的值最小,那么有d1=xi+1xi3

  现在我们要看放在哪一个区间更好,也就是可以取到最大的最小值。

  先看把牛放入中间区间的情况,假设在没放牛之前的初始情况,任意两个牛之间的距离为xi,其中x1=x2x1,以此类推。假设我们把牛放入第i个区间,得到的最小距离是yi=xi+1xi3,那么最后所有牛间的最小值就应该是min{x1,x2,,yi,xi+1,,xm},由因为xi>yi,因此我们可以把xi放入进行比较,这并不会影响最终答案,因此就有min{x1,x2,,xm,yi}。可以发现,这对于任何中间的区间都成立,只需要把yi改成对应放入的区间即可。而对于首尾区间的情况,同样只需要把yi换成首尾区间的结果。因此为了使得最小的距离最大,应该取所有的yi的最大值。假设一开始已存在的牛之间的最小距离为xmin=min{x1,x2,,xm},因此对于把牛都放入同一个区间的情况,能够取到的最大距离就为min{xmin,max{yi}}

  然后是把牛放入不同区间的情况。如果放在首尾区间,那么应该放在左端点或右端点上,距离分别为x11nxm。如果是放在中间的中,那么最小距离的最大值为xi+1xi2。我们在每一个区间中都放一头牛得到上述的距离,最后在这些距离中找到最大值和次大值,那么我们应该把这两头牛分别放入这两个对应区间中,最小距离可以取到最大值。假设最大值和次大值分别为y1y2,和第一种情况一样yi<xiyj<xj,因此能够取到的最大距离就为min{xmin,yi,yj},即min{xmin,yj}

  最后的答案应该就是求出这两种情况的结果中,取最小值。

  如果不存在首区间或尾区间,根据上面的公式,会有x11=0nxm=0,对于两种放法,我们都是取所有放入后的距离的最大值,而0并不会影响取最大值,因此也是成立的。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 1e5 + 10;
 6 
 7 char str[N];
 8 int pos[N];
 9 
10 int main() {
11     int n;
12     scanf("%d %s", &n, str + 1);
13     
14     int cnt = 0;
15     for (int i = 1; i <= n; i++) {
16         if (str[i] == '1') pos[++cnt] = i;  // 记录初始时有牛的位置
17     }
18     
19     if (cnt == 0) { // 如果一头牛的没有,那么两头牛应该放在左右端点
20         printf("%d", n - 1);
21         return 0;
22     }
23     
24     int minx = N;   // 初始时,两头牛之间的最小距离
25     for (int i = 1; i < cnt; i++) {
26         minx = min(minx, pos[i + 1] - pos[i]);
27     }
28     
29     // 把两头牛放入同一个区间
30     int y = max(pos[1] - 1 >> 1, n - pos[cnt] >> 1);    // 先对首尾区间这种特殊情况进行判断
31     for (int i = 1; i < cnt; i++) {
32         y = max(y, (pos[i + 1] - pos[i]) / 3);  // 处理中间区间的情况
33     }
34     
35     // 把两头牛放入不同的区间
36     int y1 = pos[1] - 1, y2 = n - pos[cnt]; // 先对首尾区间进行特判
37     if (y1 < y2) swap(y1, y2);  // y1是最大值,y2是次大值
38     for (int i = 1; i < cnt; i++) {
39         int t = pos[i + 1] - pos[i] >> 1;   // 处理中间区间的情况
40         
41         // 维护最大值和次大值
42         if (t >= y1) y2 = y1, y1 = t;
43         else if (t > y2) y2 = t;
44     }
45     
46     printf("%d", min(minx, max(y, y2)));
47     
48     return 0;
49 }
复制代码

  这题还可以用二分。一开始的搜索的左右端点值为left=1,right=xmin

  对于check函数,假设要判断的最小距离的最大值能否为x,先从头到尾扫描一遍序列,然后如果某个位置i0,那么把牛放入这个位置,然后判断与前一头牛和后一头牛的距离,应该要满足iprex && ineix,其中pre表示前一头牛的位置,nei为在i这个位置的后一头牛的位置,可以预处理出来。只有满足这个条件,才可以把牛放入,并更新pre为i。最后判断能否放入两头牛。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int N = 1e5 + 10, INF = 0x3f3f3f3f;
 7 
 8 int n;
 9 char str[N];
10 int a[N], ne[N];
11 
12 bool check(int x) {
13     int cnt = 0;
14     for (int i = 1, pre = -INF; i <= n; i++) {  // 一开始pre设为负无穷,意味着在最左边有一头牛
15         if (a[i] == 0 && i - pre >= x && ne[i] - i >= x) {
16             if (++cnt >= 2) return true;
17             pre = i;
18         }
19         else if (a[i] == 1) {
20             pre = i;
21         }
22     }
23     
24     return false;
25 }
26 
27 int main() {
28     scanf("%d %s", &n, str + 1);
29     
30     int left = 1, right = N;
31     for (int i = 1, pre = -INF; i <= n; i++) {
32         a[i] = str[i] - '0';
33         if (a[i] == 1) {
34             right = min(right, i - pre);
35             pre = i;
36         }
37     }
38     
39     // 预处理出来每头牛的下一头牛的位置,在正无穷处设一头牛
40     memset(ne, 0x3f, sizeof(ne));
41     for (int i = n; i; i--) {
42         if (a[i] == 0) {
43             if (a[i + 1] == 1) ne[i] = i + 1;
44             else ne[i] = ne[i + 1];
45         }
46     }
47     
48     while (left < right) {
49         int mid = left + right + 1 >> 1;
50         if (check(mid)) left = mid;
51         else right = mid - 1;
52     }
53     
54     printf("%d", left);
55     
56     return 0;
57 }
复制代码

 

参考资料

  AcWing 1659. 社交距离 I(春季每日一题2022):https://www.acwing.com/video/3747/

  AcWing 1659. 社交距离 I 二分:https://www.acwing.com/solution/content/103535/

posted @   onlyblues  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
Web Analytics
点击右上角即可分享
微信分享提示