[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';
}