题目描述
由于高传染性的牛传染病 COWVID-19 的爆发,Farmer John 非常担忧他的奶牛们的健康。
尽管他尽了最大努力使他的 N 头奶牛们践行“社交距离”,还是有许多奶牛不幸染上了疾病。
编号为 1…N 的奶牛们分别位于一条长直道路上的不同位置(相当于一维数轴),奶牛 i 位于位置 xi。
Farmer John 知道存在一个半径 R,任何与一头被感染的奶牛距离不超过 R 单位的奶牛也会被感染(然后会传染给与其距离 R 单位内的奶牛,以此类推)。
不幸的是,Farmer John 并不确切知道 R 的值。
他只知道他的哪些奶牛被感染了。
给定这个数据,求出起初感染疾病的奶牛的最小数量。
输入格式
输入的第一行包含 N。
以下 N 行每行用两个整数 x 和 s 描述一头奶牛,其中 x 为位置,s 为 0 表示健康的奶牛,1 表示染病的奶牛,并且所有可能因传播而染病的奶牛均已染病。
输出格式
输出在疾病开始传播之前已经得病的奶牛的最小数量。
数据范围
1≤N≤1000,
0≤x≤106
样例
输入样例:
6
7 1
1 1
15 1
3 1
10 0
6 1
输出样例:
3
思路:
(刚开始我也没想那么多,就去模拟了一下,发现同样是找下一个不同的状态,有的像双指针hhh)
运用下y总之前讲类似题目的思路,首先存在一个半径 R,任何与一头被感染的奶牛距离不超过 R 单位的奶牛也会被感染(然后会传染给与其距离 R 单位内的奶牛,以此类推)。
大致分成几段区间,可以认为每段区间内的奶牛会被感染;
如果俩个区间相互包含,我们可以分出俩段不相交的的区间以之代替;
如果俩个区间相交,那我们也可以分出俩段不相交的区间;
那么我们可以找出至少一个最优解是满足每段区间不相交的性质;
所有在我们的所有范围内,区间只可能被覆盖1次或0次;
首先先求出最小的R ——这个枚举一下;
双指针模板:(附注:上次讲错了时间复杂度,应该是O(n))
for(int i=0,j=0;i<n;i++)
{
while(j<i && check(i,j)) j++;
//每道题目的具体逻辑
}
朴素解法:
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
优化核心在于i和j的变化规律——单调性;
其实奶牛是否感染,跟R有关;
我们关注的是在寻找每头奶牛与旁边奶牛距离之差和R的关系
如果超出距离R说明感染不到,否则引起感染;
相当于求感染群体有几个,本题中求得是至少几头牛感染,它们的结果正好是相同的;
那么我们会发现j相对于i始终是向前的;
由此可以用双指针;
时间复杂度 O(n)
参考文献
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6+10;
int n,ans;
vector<PII>s;
int main()
{
cin>>n;
for (int i = 0; i < n; i ++ )
{
int x,y;
cin>>x>>y;
s.push_back({x,y});
}
sort(s.begin(),s.end());
int r_min=1e7;
int t=s[0].y;
for (int i = 1; i < n; i ++ )
{
if(t!=s[i].y) r_min=min(r_min,s[i].x-s[i-1].x-1);
t=s[i].y;
}
for (int i = 0; i < n; i ++ )
{
if(s[i].y)
{
ans++;
int j=i+1;
while(j<n&&s[j].x-s[j-1].x<=r_min)j++;
i=j-1;
}
}
return cout<<ans,0;
}
Java代码
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Node
{
int x;
int f;
Node(int x, int f)
{
this.x=x;
this.f=f;
}
}
public class Main
{
public static void main(String [] args)
{
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
List<Node> nums = new ArrayList<>();
for (int i = 0; i < n; i ++)
{
int x = cin.nextInt();
int f = cin.nextInt();
nums.add(new Node(x, f));
}
Collections.sort(nums, (o1, o2) -> o1.x - o2.x);
int R = Integer.MAX_VALUE;
for (int i = 0; i < n - 1; i ++)
{
if (nums.get(i).f != nums.get(i + 1).f)
{
R = Math.min(R, nums.get(i + 1).x - nums.get(i).x - 1);
}
}
int ans = 0;
for(int i=0;i<n;i++)
{
if (nums.get(i).f == 1)
{
ans ++;
int j=i+1;
while (j < n && nums.get(j).x - nums.get(j-1).x <= R)j++;
i=j-1;
}
}
System.out.println(ans);
}
}
其他大佬的解法:
思路二:模拟
Python代码
时间复杂度:O(n)
def main():
n = int(input())
nums = []
for _ in range(n):
x, f = map(int, input().split())
nums.append((x, f))
nums.sort()
R = float('inf')
for i in range(n - 1):
if nums[i][1] != nums[i + 1][1]:
R = min(R, nums[i + 1][0] - nums[i][0] - 1)
res = 0
i = 0
while i < n:
if nums[i][1] == 1:
res += 1
while i + 1 < n and nums[i + 1][0] - nums[i][0] <= R:
i += 1
i += 1
print(res)
if __name__ == "__main__":
main()
思路三:二分+双指针
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
#define pii pair<int, int>
#define fir first
#define pb push_back
#define sec second
int n;
vector<pii> a;
bool check (int tar) {
bool f =1;
for (int i = 1; i <= n; i ++) {
if (a[i].sec) continue;
if (i == 1) {
if (a[i + 1].sec&& a[i + 1].fir - a[i].fir <= tar) return 0;
}
else if (i == n ) {
if (a[i - 1].sec && a[i].fir - a[i - 1].fir <= tar) return 0;
}
else {
if (a[i - 1].sec)
if (a[i].fir - a[i - 1].fir <= tar) return 0;
if (a[i + 1].sec)
if (a[i + 1].fir - a[i].fir <= tar) return 0;
}
}
return 1;
}
void solve() {
cin >>n;
a.pb({0, 0});
for (int i = 0; i< n ;i ++)
{
int x, y;
cin >> x >> y;
a.pb({x, y});
}
sort(a.begin(), a.end());
int l = 0, r = 1e9;
while (l < r)
{
int mid = l + r + 1>> 1;
if (check(mid))l = mid;
else r = mid - 1;
}
//二分找到最大的满足要求的R
int ans = 0;
for (int i = 1, j = 2; i <= n; i++)
{
j = i + 1;
while (j <= n && a[j].fir - a[j - 1].fir <= l) j ++;
ans++;
i = j - 1;
}
//双指针找到分组个数
cout << ans << endl;
}
int main () {
int t;
t = 1;
while (t --) solve();
return 0;
}
//二分找到最大的r。然后用双指针扫描。
思路四:枚举+贪心(这大佬的解法真的是nb ,hhh)
#include<iostream>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
const int N=1010;
typedef pair<int,int>PII;
PII a[N];
int main()
{
int n;
scanf("%d",&n);
int distance;
for(int i=0;i<n;i++)
{
scanf("%d %d",&a[i].x,&a[i].y);
}
sort(a,a+n);//排序
for(int i=0;i<n;i++) //找到R
{
if(!a[i].y)
{
if(!i&&a[i+1].y)distance=min(distance,a[i+1].x-a[i].x);
if(i&&a[i-1].y)distance=min(distance,a[i].x-a[i-1].x);
if(i&&a[i+1].y)distance=min(distance,a[i+1].x-a[i].x);
}
}
int cnt=0;
for(int i=0;i<n;i++)//分情况找到不同的集合 可以看作昨天的每日一题 求不同子段的数量
{
if(!i&&a[i].y)cnt++; //第一个为1
if(i&&!a[i-1].y&&a[i].y)cnt++; //前者和现在 一个为0 一个为1
if(i&&a[i-1].y&&a[i].y&&a[i].x-a[i-1].x>=distance)cnt++;
//前者 和现在都为1但距离>=R
}
cout<<cnt;
}
思路五:map+枚举
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
map<int,int>mp;
int main()
{
int n,x,y;
cin>>n;
for(int i=0;i<n;++i){
cin>>x>>y;
mp[x]=y;
}
int now=-1,f=-1,len=INT_MAX;
//先计算出R的最小值
for(auto& i:mp){
if(now==-1){
now=i.first;
f=i.second;
}
else{
if(i.second!=f){
len=min(len,i.first-now);
}
now=i.first;
f=i.second;
}
}
int ans=0;
now=-1;
for(auto& i:mp){
if(now==-1){
now=i.first;
f=i.second;
if(f==1)
ans++;
}
else{
if(i.second==f&&f==1){
if(i.first-now>=len) //两头患病牛之间距离大于等于R ans++
ans++;
}
else if(i.second!=f&&f==0)
//不患病的牛遇到患病的牛 说明另一边肯定有至少一头牛患病 ans++
ans++;
now=i.first;
f=i.second;
}
}
cout<<ans<<endl;
return 0;
}
欢迎留言点赞
嗷嗷嗷