【贪心】线段覆盖

139 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

线段覆盖

题目背景

快 noip 了,yyy 很紧张!

题目描述

现在各大 oj 上有 n 个比赛,每个比赛的开始、结束的时间点是知道的。

yyy 认为,参加越多的比赛,noip 就能考的越好(假的)。

所以,他想知道他最多能参加几个比赛。

由于 yyy 是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加 2 个及以上的比赛。

输入格式

第一行是一个整数 n ,接下来 n 行每行是 2 个整数 a_{i},b_{i} ( a_{i}<b_{i} ),表示比赛开始、结束的时间。

输出格式

一个整数最多参加的比赛数目。

样例 #1

样例输入 #1

 3
 0 2
 2 4
 1 3

样例输出 #1

 2

提示

对于20%的数据,n10对于 20\% 的数据, n \le 10。

对于50%的数据,n103对于 50\% 的数据, n \le 10^3。

对于70%的数据,n105对于 70\% 的数据, n \le 10^{5}。

对于100%的数据,1n1060ai<bi106对于 100\% 的数据, 1\le n \le 10^{6} , 0 \le a_{i} < b_{i} \le 10^6。

解题思路

贪心

在一个数轴上有n条线段,现要选取其中k条线段使得这k条线段两两没有重合部分,问最大的k为多少。

贪心策略: 结束时间早(尽可能不影响后面的比赛)的在前,若结束时间相同,则开始时间晚(尽可能不影响前面的比赛)的在前。

 #include<bits/stdc++.h>
 using namespace std;
 ​
 int main() {
     int n;
     cin >> n;
     vector<pair<int,int>> v(n);
     for(auto& [l, r] : v) {
         cin >> l >> r;
     }
     sort(v.begin(), v.end(), [&](const auto& x, const auto& y){
         return x.second < y.second; 
     });
     int ret = 0, cur = -1;
     for(const auto& [l, r] : v) {
         if(l >= cur) {
             ret += 1;
             cur = r;
         }
     }
     cout << ret << endl;
     return 0;
 }

DP

定义:f[i]f[i]表示在前ii场比赛中最多可以参加几场比赛,

由此得出方程: f[i]=max(f[i1],f[last]+1)f[i]=max(f[i-1],f[last]+1)

f[last]f[last]指从f[i1]f[i-1]向前找到的第一个允许参加第ii场比赛

 #include<bits/stdc++.h>
 using namespace std;
 ​
 int main() {
     int n;
     cin >> n;
     vector<pair<int,int>> v(n);
     for(auto& [l, r] : v) {
         cin >> l >> r;
     }
     sort(v.begin(), v.end(), [&](const auto& x, const auto& y){
         return x.second < y.second; 
     });
     vector<int> f(n, 0);
     f[0] = 1;
     int last = -1;
     for(int i = 1; i < n; i++) {
         while(v[last + 1].second <= v[i].first) last++;
         f[i] = max(f[i - 1], f[last] + 1);
     }
     cout << f[n - 1] << endl;
     return 0;
 }