POJ 1727 Advanced Causal Measurements (ACM)(二分+贪心)

来源:互联网 发布:区块链 拜占庭算法 编辑:程序博客网 时间:2024/05/22 10:48

Description
有n个事件,每个事件用其发生的位置s1和时间t1来描述,而每一个事件都有其因事件,若一个事件位置s2和时间t2满足|t2-t1|>=|x2-x1|,则该事件成为原事件的因事件。现给出m个因事件,对于这m个”因事件”点集,可能是所有点的因事件,也可能是某个或某几个点的因事件。求全部因事件点中时间最早的那个点的时间最大值
Input
多组用例,第一行为用例组数T,对于每组用例,第一行包括事件数n和因事件数m,之后n行为n个时间的发生的时间和位置
Output
对于每组用例,输出其因事件中最早发生时间
Sample Input
4
4 1
1 -1
1 3
1 4
2 6
4 2
1 -1
1 3
1 4
2 6
4 3
1 -1
1 3
1 4
2 6
4 4
1 -1
1 3
1 4
2 6
Sample Output
Case 1: -2
Case 2: 0
Case 3: 0
Case 4: 1
Solution
记点(x,y)代表一个事件,x为事件发生位置,y为事件发生时间。在坐标系中以一个事件的坐标点做斜率分别为1和-1的两条直线,则在这两条直线下方的点即为该事件的因事件取值范围。故此题转化为在所有事件点中所对应的因事件范围内找m个点,使这m个点能够导致这n个事件发生,输出m个因事件中时间最大值。
那么二分目标时间,求出每个点的因事件在y=t这条直线上可能存在的坐标范围,遍历排序后的所有点,尽可能的计算出相邻两个点的可能范围的相交区间,若没有相交,则需要新增一个因事件点。这样算出来需要的总点数如果不超过m则记录答案,然后继续二分。
Code

#include<cstdio>#include<iostream>#include<algorithm>using namespace std;#define maxn 100010struct node{    int x,y;}P[maxn];int T,n,m;int cmp(node a,node b){    if(a.x==b.x)//位置相同则按时间升序排         return a.y<b.y;    return a.x<b.x;//否则按位置升序排 }bool OK(int t){    int ll=t-P[0].y+P[0].x;//ll为第一个事件在y=t上因事件范围左端点     int rr=P[0].y-t+P[0].x;//rr为第一个事件在y=t上因事件范围右端点    int count=1;//初始为一个因事件     for(int i=1;i<n;i++)    {        int l=t-P[i].y+P[i].x;//l为第i个事件在y=t上因事件范围左端点         int r=P[i].y-t+P[i].x;//r为第i个事件在y=t上因事件范围右端点         if(rr<l)//上一个事件的因事件均不能作为该事件的因事件         {            count++;//因事件个数加一             rr=r;//更新因事件范围右端点             ll=l;//更新因事件范围左端点         }        else if(ll<=l&&rr>=l&&rr<=r)//两个时间的因事件范围有重叠             ll=l;//更新因事件范围左端点          else if(ll<=l&&rr>r)//上一个事件的因事件范围覆盖该事件的因事件范围         {            rr=r;//更新因事件范围右端点             ll=l;//更新因事件范围左端点        }        if(count>m)//因事件个数大于m,返回false             return false;    }    return true;//因事件个数小于m,返回true }int main(){    scanf("%d",&T);    int res=0;    while(T--)    {        scanf("%d%d",&n,&m);        scanf("%d%d",&P[0].y,&P[0].x);//先输入时间,后输入位置         int l=-2000020,r=P[0].y;        for(int i=1;i<n;i++)        {            scanf("%d%d",&P[i].y,&P[i].x);            r=min(r,P[i].y);//二分右极限为所有事件发生时间最小值         }        int ans;        sort(P,P+n,cmp);//对事件点排序         while(l<=r)//二分目标时间         {            int mid=(l+r)/2;            if(OK(mid))            {                ans=mid;                l=mid+1;            }            else                r=mid-1;        }        printf("Case %d: %d\n",++res,ans);//按格式输出     }    return 0;}
0 0