Link
https://www.lydsy.com/JudgeOnline/problem.php?id=2436
题意
安排
n
n
n 个活动,每个活动举办时间
(
s
i
,
t
i
)
(s_i,t_i)
(si,ti) ,同一时间两个场地只能有一个办活动(可以办好多个)。
对于每个询问,钦定一个活动必须举办(或者不钦定),要求最大化举办的活动数量较少的场地举办的活动数量。
输出这个最大值。
好 我又老了
上来先排序离散化
考虑到实际上一个会场选的是“一段时间”,可以预处理出在某一段时间内开始并结束的活动数量
p
(
a
,
b
)
p(a,b)
p(a,b)
很容易根据上面这东西大概地对转移有个猜想,然后就差不多可以设计出状态:
可以考虑设类似于
f
(
k
,
i
)
f(k,i)
f(k,i) ,那么前
k
k
k 个时间,一个场地至多选
i
i
i 个的时候另一个场地至多选
f
(
k
,
i
)
f(k,i)
f(k,i) 个
为了方便钦定,相应地考虑可能会设
g
(
k
,
i
)
g(k,i)
g(k,i) ,那么第
k
k
k 个时间之后,一个场地至多选
i
i
i 个的时候下略
考虑转移,
f
(
k
,
i
)
=
max
d
∈
[
0
,
k
)
max
{
f
(
d
,
i
)
+
p
(
d
,
k
)
,
f
(
d
,
i
−
p
(
d
,
k
)
)
}
f(k,i)=\max\limits_{d\in[0,k)}\max\{ f(d,i)+p(d,k),f(d,i-p(d,k))\}
f(k,i)=d∈[0,k)maxmax{f(d,i)+p(d,k),f(d,i−p(d,k))}
那么
f
f
f 和
g
g
g 都可以
O
(
n
3
)
O(n^3)
O(n3) 搞出来
然后考虑不加限制时的答案:暴扫一遍都可以搞出来了(。。。
钦定一个活动 等于 钦定了那一整段时间内的都要选
然后两边的怎么做?
如果我们钦定的给某一个场地办,对于另外一个场地可以考虑枚举在钦定左右分别选了几个
好像也搞完了?
a
n
s
(
i
,
j
)
=
max
x
,
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)\}\}
ans(i,j)=x,ymax{min{x+y,f(i,x)+p(i,j)+g(j,y)}}
O
(
n
3
)
O(n^3)
O(n3) ……?不对。
最终答案并不等于
a
n
s
(
s
i
,
t
i
)
ans(s_i,t_i)
ans(si,ti) 。
(啊啊啊。。要是考起来我大概就被这东西坑爆了)
钦定了
(
L
,
R
)
(L,R)
(L,R) ,但是实际上选的区域可能会更大 比如
(
l
,
r
)
,
l
<
L
<
R
<
r
(l,r),l<L<R<r
(l,r),l<L<R<r 这样
显然我们钦定还是有影响到
(
l
,
L
)
(l,L)
(l,L) 和
(
R
,
r
)
(R,r)
(R,r)
所以我们需要处理出所有的
a
n
s
(
i
,
j
)
ans(i,j)
ans(i,j) ,实际上是
O
(
n
4
)
O(n^4)
O(n4) 的
然后可以搞记忆化搞剪枝等等玄学一发小常数骗分(甚至可能水掉这道题
(我作为一个不会水法选手 记笔记记笔记.jpg
(但是感觉我反而会玄学剪枝剪到常数变大啊(???
关于这部分中间差点掉坑,其实如果运气好是可以避免的
思考钦定某个活动要办,然后考虑枚举一下某个场地覆盖这个活动的那段启用的时间
好像也挺自然的(?)
怎么优化成
O
(
n
3
)
O(n^3)
O(n3) ?
dp 优化当然要考虑单调性啦,考虑
a
n
s
(
i
,
j
)
=
max
x
,
y
min
ans(i,j)=\max\limits_{x,y}\min
ans(i,j)=x,ymaxmin
思考
x
,
y
x,y
x,y 最优决策点,那先固定
i
,
j
i,j
i,j
不难发现
f
(
i
,
x
)
f(i,x)
f(i,x) 和
g
(
j
,
y
)
g(j,y)
g(j,y) 在
i
i
i 固定的时候有显然的单调性
换成
a
n
s
(
i
,
j
)
=
max
x
,
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\}\}
ans(i,j)=x,ymax{min{f(i,x)+g(j,y),x+p(i,j)+y}}
所以对于最优决策,按照
x
,
y
x,y
x,y 中的某一个排序后,按顺序看
x
,
y
x,y
x,y 必定是一个变大一个变小
那么维护指针就可以
O
(
n
)
O(n)
O(n) 找出所有最优决策点对
(
x
,
y
)
(x,y)
(x,y)
复杂度
O
(
n
3
)
O(n^3)
O(n3)
#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;
}