P1868 饥饿的奶牛
P1868 饥饿的奶牛
原题链接:传送门
分析
首先根据数据范围可知此问题的时间复杂度大概限制\(O(n) / O(nlong_n)\) 左右。由于是问的最多往贪心/dp方向思考
题目中指出可以选择任意区间但是这些区间不能有重叠的部分
对于一个区间[l, r],一共有两种可能
我们假设\(f(i)\) 表示前 i 个区间可以获取的最大价值
-
选择这个区间
如果我们要选择第 i 个区间,那么我们需要前 i - 1 个区间可以获取的最大价值
即 \(f(i) = f(k) + r - l + 1 (k 为上一个紧接着的区间)\)
-
不选择这个区间
\(f(i) = f(i-1)\)
那么问题的关键就在于如何找到第一种情况中的 k 的下标。我们可以根据区间的 r 值对这个所有区间进行一次排序操作。
对于每一个区间\([l,r]\),我们只需要找到最后一个 k < r 的区间的下标
这个问题是一个线性dp问题其中串联使用了二分来搜索状态
从状态的定义到状态的寻找,从dp-->二分环环相扣。
AC 代码
C++ code
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
typedef long long ll;
struct node{
int l, r;
}e[N];
ll f[N], n;
bool cmp(node a,node b)
{
return a.r < b.r;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> e[i].l >> e[i].r;
}
sort(e + 1, e + 1 + n, cmp);
for(int i = 1; i <= n; i ++)
{
ll w = e[i].r - e[i].l + 1;
f[i] = w;
int l = 1, r = i - 1;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(e[mid].r < e[i].l)l = mid;
else r = mid - 1;
}
if(e[l].r < e[i].l)
{
f[i] = max(f[i-1], f[i] + f[l]);
} else {
f[i] = max(f[i], f[i-1]);
}
}
cout << f[n] << endl;
return 0;
}