洛谷P3084 [USACO13OPEN]照片

题目

\(DP\)

设状态\(dp[i]\)\(i\)位置放了斑点牛,前\(i\)个位置能得到的最多的牛。

有方程\(dp[i]=max(dp[j]+1,dp[i])\),而我们并不知道什么\(j\)可以使\(i\)不在区间内

由于\(i\)位置放了牛,又因为\(i\)所在的区间只能放一个,\(j\)不能跟\(i\)同属于一个区间,又因为每个区间有且都有一个斑点牛,因此,每个区间都要有一个数,对于每个\(i\),都会有一段区间里的下标\(j\)可以用来转移\(i\),现在问题转化为了如何求对于每个\(i\)所对应的\(j\)的所在的区间问题。

每个区间不会有两个斑点牛此条件:首先\(j\)一定比\(i\)之后的所有位置的所在的最左边的左端点的区间要小这是显然的,设此左端点\(l\)\(i\)对应的\(j\)最大为多少。

每个区间至少有一个斑点牛此条件:因为已经枚举到了第\(i\)个位置,因此\(r\)\(i\)小的区间一定要有值,因此\(j\)一定要在一个\(r\)\(i\)小且\(l\)最大的\(l\)右边,因为我们是动态规划,所以我们只需要用到\(dp[j]\)这个状态,而且这个状态在之前算\(dp[j]\)的时候就已经符合题目要求的了,而且\(j\)一定属于\(i\)之前的\(l\)最大的区间

#include <bits/stdc++.h>
#define N 2010010
using namespace std;
deque <int> q;
int n, m, ans, dp[N], l[N], r[N];//dp[i]表示i位置放斑点牛,前i个位置所得到的最多的牛。
//r[i]代表着i之后的区间的最小左端点。j一定不能比r[i]大 
//l[i]代表着i之前的区间的最大左端点。j同时不能比l[i]小,因为如果j比l[i]小的话,那 
inline void init()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n + 1; i++)
		r[i] = i;
	for (int i = 1; i <= m; i++)
	{
		int a, b;
		scanf("%d%d", &a, &b);
		r[b] = min(a, r[b]);//b位置的j取值一定要比r小
		l[b + 1] = max(a, l[b + 1]);//b+1的j位置一定要比l大
	}
	for (int i = n; i >= 1; i--)//此i的r值必须要比i+1小,因为i+1取不到的,i也不能取到。 
		r[i] = min(r[i + 1], r[i]);
	for (int i = 2; i <= n + 1; i++)//此i的l值必须必i-1大,因为首先需要满足i-1位置的值。 
		l[i] = max(l[i - 1], l[i]);
}
int main()
{
	init();
	q.push_back(0);
	int j = 1;
	for (int i = 1; i <= n + 1; i++)
	{
		while (j < r[i])//j首先要比r小, 
		{
			if (dp[j] != -1)
			{
				while (!q.empty() && dp[j] > dp[q.back()]) 
					q.pop_back();
				q.push_back(j);
			}
			j++;
		}
		while (!q.empty() && q.front() < l[i])//其次要比l大 
			q.pop_front();
		if (!q.empty()) dp[i] = max(dp[i], dp[q.front()] + (int) (i != n + 1) );
		else dp[i] = -1;
	}
	printf("%d", dp[n + 1] ? dp[n + 1] : -1);
	return 0;
}
posted @ 2019-11-09 08:33  刘文尧  阅读(127)  评论(0编辑  收藏