[USACO13OPEN]照片Photo 區間唯一存在

[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';
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章