[BZOJ2436] [NOI2011] Noi嘉年華 [單調性優化DP]

Link
https://www.lydsy.com/JudgeOnline/problem.php?id=2436


題意
安排 nn 個活動,每個活動舉辦時間 (si,ti)(s_i,t_i) ,同一時間兩個場地只能有一個辦活動(可以辦好多個)。
對於每個詢問,欽定一個活動必須舉辦(或者不欽定),要求最大化舉辦的活動數量較少的場地舉辦的活動數量。
輸出這個最大值。


好 我又老了

上來先排序離散化
考慮到實際上一個會場選的是“一段時間”,可以預處理出在某一段時間內開始並結束的活動數量 p(a,b)p(a,b)
很容易根據上面這東西大概地對轉移有個猜想,然後就差不多可以設計出狀態:
可以考慮設類似於 f(k,i)f(k,i) ,那麼前 kk 個時間,一個場地至多選 ii 個的時候另一個場地至多選 f(k,i)f(k,i)
爲了方便欽定,相應地考慮可能會設 g(k,i)g(k,i) ,那麼第 kk 個時間之後,一個場地至多選 ii 個的時候下略

考慮轉移, f(k,i)=maxd[0,k)max{f(d,i)+p(d,k),f(d,ip(d,k))}f(k,i)=\max\limits_{d\in[0,k)}\max\{ f(d,i)+p(d,k),f(d,i-p(d,k))\}
那麼 ffgg 都可以 O(n3)O(n^3) 搞出來
然後考慮不加限制時的答案:暴掃一遍都可以搞出來了(。。。


欽定一個活動 等於 欽定了那一整段時間內的都要選
然後兩邊的怎麼做?
如果我們欽定的給某一個場地辦,對於另外一個場地可以考慮枚舉在欽定左右分別選了幾個
好像也搞完了?
ans(i,j)=maxx,y{min{x+y,f(i,x)+p(i,j)+g(j,y)}}ans(i,j)=\max\limits_{x,y}\{\min\{x+y,f(i,x)+p(i,j)+g(j,y)\}\}
O(n3)O(n^3) ……?不對。

最終答案並不等於 ans(si,ti)ans(s_i,t_i)
(啊啊啊。。要是考起來我大概就被這東西坑爆了)

欽定了 (L,R)(L,R) ,但是實際上選的區域可能會更大 比如 (l,r),l<L<R<r(l,r),l<L<R<r 這樣
顯然我們欽定還是有影響到 (l,L)(l,L)(R,r)(R,r)
所以我們需要處理出所有的 ans(i,j)ans(i,j) ,實際上是 O(n4)O(n^4)
然後可以搞記憶化搞剪枝等等玄學一發小常數騙分(甚至可能水掉這道題
(我作爲一個不會水法選手 記筆記記筆記.jpg
(但是感覺我反而會玄學剪枝剪到常數變大啊(???

關於這部分中間差點掉坑,其實如果運氣好是可以避免的
思考欽定某個活動要辦,然後考慮枚舉一下某個場地覆蓋這個活動的那段啓用的時間
好像也挺自然的(?)


怎麼優化成 O(n3)O(n^3)
dp 優化當然要考慮單調性啦,考慮 ans(i,j)=maxx,yminans(i,j)=\max\limits_{x,y}\min
思考 x,yx,y 最優決策點,那先固定 i,ji,j
不難發現 f(i,x)f(i,x)g(j,y)g(j,y)ii 固定的時候有顯然的單調性
換成 ans(i,j)=maxx,y{min{f(i,x)+g(j,y),x+p(i,j)+y}}ans(i,j)=\max\limits_{x,y}\{\min\{f(i,x)+g(j,y),x+p(i,j)+y\}\}
所以對於最優決策,按照 x,yx,y 中的某一個排序後,按順序看 x,yx,y 必定是一個變大一個變小
那麼維護指針就可以 O(n)O(n) 找出所有最優決策點對 (x,y)(x,y)
複雜度 O(n3)O(n^3)


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
const int MAXN = 405;
int n, s[MAXN], t[MAXN], tmp[MAXN], m;
int f[MAXN][MAXN], p[MAXN][MAXN], ans[MAXN][MAXN], g[MAXN][MAXN];
int main()
{
	scanf("%d", &n);
	for (register int i = 1; i <= n; ++i)
	{
		scanf("%d%d", &s[i], &t[i]);
		t[i] += s[i];
		tmp[++m] = s[i], tmp[++m] = t[i];
	}
	sort(tmp + 1, tmp + 1 + m);
	m = unique(tmp + 1, tmp + 1 + m) - tmp - 1;
	for (register int i = 1; i <= n; ++i)
	{
		s[i] = lower_bound(tmp + 1, tmp + 1 + m, s[i]) - tmp;
		t[i] = lower_bound(tmp + 1, tmp + 1 + m, t[i]) - tmp;
		for (register int l = 1; l <= s[i]; ++l)
		for (register int r = t[i]; r <= m; ++r)
			++p[l][r];
	}
	for (register int i = 1; i <= m; ++i)
	for (register int j = 1; j <= n; ++j)
		f[i][j] = g[i][j] = -0x3f3f3f3f;
	for (register int k = 1; k <= m; ++k)
	for (register int i = 0; i <= p[1][k]; ++i)
	for (register int d = 1; d <= k; ++d)
	{
		f[k][i] = max(f[k][i], f[d][i] + p[d][k]);
		if (i >= p[d][k]) f[k][i] = max(f[k][i], f[d][i-p[d][k]]);
	}
	for (register int k = m; k >= 1; --k)
	for (register int i = 0; i <= p[k][m]; ++i)
	for (register int d = k; d <= m; ++d)
	{
		g[k][i] = max(g[k][i], g[d][i] + p[k][d]);
		if (i >= p[k][d]) g[k][i] = max(g[k][i], g[d][i-p[k][d]]);
	}
	for (register int i = 1; i <= m; ++i)
	for (register int j = i + 1; j <= m; ++j)
	for (register int fa, c, x = 0, y = n; x <= n; ++x)
	{
		fa = min(x + p[i][j] + y, f[i][x] + g[j][y]);
		while (y)
		{
			c = min(x + p[i][j] + y - 1, f[i][x] + g[j][y - 1]);
			if (fa <= c) fa = c, --y;
			else break;
		}
		ans[i][j] = max(ans[i][j], min(x + p[i][j] + y, f[i][x] + g[j][y]));
	}
	int Ans = 0;
	for (register int i = 1; i <= n; ++i) Ans = max(Ans, min(f[m][i], i));
	printf("%d\n", Ans);
	for (register int k = 1; k <= n; ++k)
	{
		Ans = 0;
		for (register int i = 1; i <= s[k]; ++i)
		for (register int j = m; j >= t[k]; --j)
			Ans = max(Ans, ans[i][j]);
		printf("%d\n", Ans);
	}
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章