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