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;
}
posted @ 2021-05-01 22:21  _starsky  阅读(91)  评论(0编辑  收藏  举报