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

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 100010
struct 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;
}

你可能感兴趣的:(POJ 1727 Advanced Causal Measurements (ACM)(二分+贪心))