【算法学习笔记】56.区间合并问题变形 SJTU OJ 1365 最少道路

Description

有一些同学在练习匀速跑,他们起点和速度各不相同,但约定好练习相同的时间。

请根据给出的同学的初始位置和速度,计算至少需要多少条跑道才能保证同学之间互相不会相撞。在任何一个时间点两个不同的同学处在相同的位置均视为相撞。

Input Format

第一行:两个整数N和T。N (1 <= N <= 1,000,000) 代表总人数,T (1 <= T <= 1,000,000,000)代表所有人跑步的时间。

第2 ~ (N+1)行:两个整数,第一个代表起始位置,第二个代表速度。

Output Format

一个整数,输出最小跑道数量。

Sample Input:

5 3
0 1
1 2
2 3
3 2
6 1

Sample Output:

3


其实。。这个题的数据是按照起点排序过后的,所以不必再进行排序了。
判断两个人是否会相交,可以根据在直角坐标系中两条线段是否相交的关系来确定检查函数
另外这个题的数据比较大,要用ULL,而且是每一个中间计算结果也要用ULL
具体算法是有点类似于贪心算法
策略就是每条新的道路尝试加入所有还未处理的人,如果都没法加入了,开一个新的道路。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;

struct Period
{
    ULL start;
    ULL end;
    bool done;
};


bool cmp_period(const Period& a,const Period& b){
    return a.start < b.start;
}


inline bool check(ULL s,ULL e,const Period& b){
    return ((s < b.start) and (e < b.end));
}

Period ps[1000000];

int main(int argc, char const *argv[])
{
    ULL N,T;
    cin>>N>>T;
    for (ULL i = 0; i < N; ++i)
    {
        scanf("%d",&ps[i].start);
        ULL v; scanf("%d",&v);
        ps[i].end = ps[i].start + v*T;
        ps[i].done = false;
    } 
    //sort(ps,ps+N,cmp_period);  //按照起点排序 
    ULL ans = 0;
    for (ULL i = 0; i < N; ++i) if(!ps[i].done)
    {
        ans++;//加入一个新跑道 
        ULL e = ps[i].end;
        ps[i].done = true;
        //此处的查找可以优化成二分查找 因为是排序过的
        for (ULL j = i+1; j < N; ++j) if(!ps[j].done)
        {
            if(check(ps[i].start,e,ps[j])){//判断是否会香蕉
                //如果不会相交
                e = ps[j].end; //类似区间合并
                ps[j].done = true;
            }
        }
    }

    cout<<ans<<endl;
    return 0;
}

标准程序如下:

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int main() {
  int N, T;
  cin >> N >> T;
 
  vector<long long> A;//存储道路
  for (int i = 0; i < N; i++) {
    long long x, s;
    cin >> x >> s;//读入起点x和速度s
    x = -(x + s * T); //设置x为终点的负数
    //如果没有一个道路 或者 
    //如果该人的终点 <= 最后一个道路的终点 就要新建一个道路
    //原因是因为最后一个道路的终点肯定是最大的
    if (A.empty() || (-A.back()) >= (-x) ) { 
      A.push_back(x);//新建一个道路 用终点的负数来标识道路
    } else {
      //二分查找 找到可以添加的道路
      *upper_bound(A.begin(), A.end(), x) = x; //把该人加入之前的某一个道路中
    }
  }
  cout << A.size() << endl;
  return 0;
}
标准程序



原文地址:https://www.cnblogs.com/yuchenlin/p/sjtu_oj_1365.html