[USACO13OPEN]照片Photo 区间唯一存在

19 篇文章 0 订阅

[USACO13OPEN]照片Photo


题意

给出n个奶牛和m个区间

  • m个区间包含所有的奶牛
  • 每个区间有且只有一只有斑点的奶牛

求最多有几头有斑点的奶牛


思路

区间有且只有一个
  • 区间内至多存在一个
  • 区间内至少存在一个

我们以样例为例,解说:
1 4
2 5
3 4
对于点1,

  • 由于区间内至多存在一个
    若点1为斑点奶牛,那么所有包含点1的区间都不在能有斑点奶牛
    即为包含该点的最大右区间,记为l
  • 由于区间至少存在一个
    那么第一个不包含1点的区间至少存在一个斑点奶牛
    即为不包含该点的最小的右区间,即为r

若点1存在斑点奶牛,那么点l+1到点r至少存在一只斑点奶牛

状态

dp[i]表示当i为斑点奶牛时,1-i区间内至多有几头斑点奶牛

转移方程
dp[j]=max(dp[j],dp[i]+1) (l\leq j\leq r)
优化

本题按理要线段树优化,或者反过来做单调队列

但是博主莽一发过了,可能数据比较水吧

代码

左区间
	for (int i = 0; i <= n; i++)l[i] = i + 1;
	//初始化左区间,因为每个点都存在,每个点左端点最少为i+1
	while (m--) {
		cin >> u >> v;
		l[u] = max(l[u], v + 1);
		//更新左端点,为l+1
	}
	for (int i = 1; i <= n; i++)l[i] = max(l[i - 1], l[i]);
	//更新区间(left,right)的点,包含i点的最大右端点一定比包含i-1的最大右端点大
右区间
	for (int i = 0; i <= n + 1; i++)r[i] = n + 1;
	//初始化右区间,每个点最多为n+1
	while (m--) {
		cin >> u >> v;
		l[u] = max(l[u], v + 1);
		r[u - 1] = min(r[u - 1], v);
		//更新left-1点的右端点
	}
	for (int i = n; i >= 0; i--) r[i] = min(r[i], r[i + 1]);
	//不包含第二个的一定不包含第一个
dp
int ans = -1; dp[0] = 0;
	for (int i = 0; i <= n; i++) {
		if (dp[i] == -inf)continue;	//该点不曾被推导过
		for (int j = l[i]; j <= r[i]; j++)
			dp[j] = max(dp[j], dp[i] + 1);//状态转移
	}
	int i = n;
	while (r[i] == n + 1)ans = max(ans, dp[i--]);
	//若右区间为n+1,那么他在最后一个区间,所以没有下一个区间
	//若最后一张照片,没有一个点可以被推出,为-1
	cout << ans << '\n';
AC
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 200005;
const int inf = 0X3f3f3f3f;
int l[maxn], r[maxn];
//r为不包含该点的最小的右区间
int dp[maxn];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n, m; cin >> n >> m;
	memset(dp, -0X3f, sizeof(int) * (n + 2));
	for (int i = 0; i <= n + 1; i++)r[i] = n + 1;
	for (int i = 0; i <= n; i++)l[i] = i + 1;
	int u, v;
	while (m--) {
		cin >> u >> v;
		l[u] = max(l[u], v + 1);
		r[u - 1] = min(r[u - 1], v);
	}
	for (int i = 1; i <= n; i++)l[i] = max(l[i - 1], l[i]);
	for (int i = n; i >= 0; i--) r[i] = min(r[i], r[i + 1]);//不包含第二个的一定不包含第一个
	int ans = -1; dp[0] = 0;
	for (int i = 0; i <= n; i++) {
		if (dp[i] == -inf)continue;
		for (int j = l[i]; j <= r[i]; j++)
			dp[j] = max(dp[j], dp[i] + 1);
	}
	int i = n;
	while (r[i] == n + 1)ans = max(ans, dp[i--]);
	cout << ans << '\n';
}  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值