题意:某个星球的一天有h小时,一小时有m分钟,现在有两种车要开,红色的每半小时开一次,每次要准备k分钟再开。蓝色的车有固定的时刻表,且没有准备时间,但是它与红色的车的时间重合了就会被取消掉(端点重合不取消),问你第一辆红色车的开始时间是多少才能使取消掉最小的蓝色车的数量最小。
分析:小时数h肯定使没用的,因为一辆蓝车在h1发车和h2发车不会对我们产生任何影响。同理,这辆蓝车在m1发车(m1<m/2)和它在m1+m/2的时候发车也不会对结果产生影响。
那么题目就可以简化成已经模了m/2的n个数,要找出一段k长度的区间,包含最少这些数。(端点不算)
因此立马想到尺取法。遍历这n个数。这里有一个trick,将这些数全部+m/2放入数组,然后再尺取。这样可以防止尺取死循环(一直找不到大于等于k的时间段),实际上这种情况意味着全包含。比如m是100,k是50,有9辆车都是49发车,一辆车是2发车。那么计算的时候就是有99发车,但是从49到99都可以走,中间只有一个52.这就说明只有一个2在里面。相反,如果不用这个trick,只有2和49,无论如何也找不到合格的解能大于等于50(52-49=3,49-2=47),不符合实际情况。虽然这个trick的物理意义我还不是很懂,希望大佬教教我。
代码:
#include<bits/stdc++.h>
using namespace std;
struct che
{
int pos;
int t;
}shi[200005];
int ji[100005];
int cmp1(che x1,che x2)
{
return x1.t<x2.t;
}
void rlmn()
{
int n,h,m,k;
scanf("%d%d%d%d",&n,&h,&m,&k);
int tmp;
for (int i=0;i<n;i++)
{
scanf("%d%d",&tmp,&shi[i].t);
shi[i].t%=(m/2);
shi[i].pos=i;
shi[i+n].t=shi[i].t+m/2;
shi[i+n].pos=i+n;
}
sort(shi,shi+2*n,cmp1);
int chi=1;
int tmpans;
int ans=n+1;
int jipos1,jipos2;
int l=0,r=1;
while (1)
{
while ((shi[r].t-shi[l].t)<k)
{
r++;
}
while ((shi[r].t-shi[l].t)>=k)
{
l++;
if (l>=n) break;
}
tmpans=r-l;//这种方案会误几班车
if (ans>tmpans)
{
ans=tmpans;
jipos1=l;
jipos2=r;
}
if (l>=n) break;
}
tmp=0;
for (int i=jipos1;i<jipos2;i++)
{
ji[tmp++]=shi[i%n].pos;
}
sort(ji,ji+tmp);
printf("%d %d\n",ans,shi[jipos2%(n)].t);
for (int i=0;i<tmp;i++)
{
printf("%d ",ji[i]+1);
}
printf("\n");
}
int main()
{
int T;
T=1;
while(T--)rlmn();
return 0;
}
这题花了好久,第一是尺取法不会了。。。写了一个假的,第二是想这个trick之前一直死循环。